diff --git a/.devcontainer/install-vscode.sh b/.devcontainer/install-vscode.sh index 9d4b52755d9..cc70d527acd 100755 --- a/.devcontainer/install-vscode.sh +++ b/.devcontainer/install-vscode.sh @@ -9,4 +9,4 @@ sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.mi rm -f packages.microsoft.gpg apt update -apt install -y code-insiders libsecret-1-dev libxkbfile-dev +apt install -y code-insiders libsecret-1-dev libxkbfile-dev libkrb5-dev diff --git a/.eslintplugin/code-amd-node-module.ts b/.eslintplugin/code-amd-node-module.ts new file mode 100644 index 00000000000..35c89dcb219 --- /dev/null +++ b/.eslintplugin/code-amd-node-module.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { join } from 'path'; + + +export = new class ApiProviderNaming implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + amdX: 'Use `import type` for import declarations, use `amdX#importAMDNodeModule` for import expressions' + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + const modules = new Set(); + + try { + const { dependencies, optionalDependencies } = require(join(__dirname, '../package.json')); + const all = Object.keys(dependencies).concat(Object.keys(optionalDependencies)); + for (const key of all) { + modules.add(key); + } + + } catch (e) { + console.error(e); + throw e; + } + + + const checkImport = (node: any) => { + + if (node.type !== 'Literal' || typeof node.value !== 'string') { + return; + } + + if (node.parent.importKind === 'type') { + return; + } + + if (!modules.has(node.value)) { + return; + } + + context.report({ + node, + messageId: 'amdX' + }); + } + + return { + ['ImportExpression Literal']: checkImport, + ['ImportDeclaration Literal']: checkImport + }; + } +}; diff --git a/.eslintplugin/code-import-patterns.ts b/.eslintplugin/code-import-patterns.ts index 07efb11b058..1f08bb4a4e8 100644 --- a/.eslintplugin/code-import-patterns.ts +++ b/.eslintplugin/code-import-patterns.ts @@ -180,8 +180,9 @@ export = new class implements eslint.Rule.RuleModule { const restrictions = (typeof option.restrictions === 'string' ? [option.restrictions] : option.restrictions).slice(0); if (targetIsVS) { - // Always add "vs/nls" + // Always add "vs/nls" and "vs/amdX" restrictions.push('vs/nls'); + restrictions.push('vs/amdX'); // TODO@jrieken remove after ESM is real } if (targetIsVS && option.layer) { diff --git a/.eslintplugin/code-no-native-private.ts b/.eslintplugin/code-no-native-private.ts new file mode 100644 index 00000000000..4d6be23b8f3 --- /dev/null +++ b/.eslintplugin/code-no-native-private.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; + +export = new class ApiProviderNaming implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + slow: 'Native private fields are much slower and should only be used when needed. Ignore this warning if you know what you are doing, use compile-time private otherwise. See https://github.com/microsoft/vscode/issues/185991#issuecomment-1614468158 for details', + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + return { + ['PropertyDefinition PrivateIdentifier']: (node: any) => { + context.report({ + node, + messageId: 'slow' + }); + }, + ['MethodDefinition PrivateIdentifier']: (node: any) => { + context.report({ + node, + messageId: 'slow' + }); + } + }; + } +}; diff --git a/.eslintplugin/code-no-test-async-suite.ts b/.eslintplugin/code-no-test-async-suite.ts new file mode 100644 index 00000000000..41d15d28636 --- /dev/null +++ b/.eslintplugin/code-no-test-async-suite.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TSESTree } from '@typescript-eslint/experimental-utils'; +import * as eslint from 'eslint'; + +function isCallExpression(node: TSESTree.Node): node is TSESTree.CallExpression { + return node.type === 'CallExpression'; +} + +function isFunctionExpression(node: TSESTree.Node): node is TSESTree.FunctionExpression { + return node.type.includes('FunctionExpression'); +} + +export = new class NoAsyncSuite implements eslint.Rule.RuleModule { + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + function hasAsyncSuite(node: any) { + if (isCallExpression(node) && node.arguments.length >= 2 && isFunctionExpression(node.arguments[1]) && node.arguments[1].async) { + return context.report({ + node: node as any, + message: 'suite factory function should never be async' + }); + } + } + + return { + ['CallExpression[callee.name=/suite$/][arguments]']: hasAsyncSuite, + }; + } +}; diff --git a/.eslintrc.json b/.eslintrc.json index c28784041b4..9e8e0cffe62 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -69,6 +69,7 @@ } ], "local/code-translation-remind": "warn", + "local/code-no-native-private": "warn", "local/code-no-nls-in-standalone-editor": "warn", "local/code-no-standalone-editor": "warn", "local/code-no-unexternalized-strings": "warn", @@ -119,6 +120,7 @@ ], "rules": { "local/code-no-test-only": "error", + "local/code-no-test-async-suite": "warn", "local/code-no-unexternalized-strings": "off" } }, @@ -193,6 +195,14 @@ ] } }, + { + "files": [ + "src/**/{common,browser}/**/*.ts" + ], + "rules": { + "local/code-amd-node-module": "warn" + } + }, { "files": [ "src/**/*.ts" @@ -595,6 +605,12 @@ "vs/workbench/workbench.common.main" ] }, + { + "target": "src/vs/amdX.ts", + "restrictions": [ + "vs/base/common/*" + ] + }, { "target": "src/vs/workbench/{workbench.desktop.main.nls.js,workbench.web.main.nls.js}", "restrictions": [] diff --git a/.github/classifier.json b/.github/classifier.json index 388a50d8db9..6710c3304da 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -281,7 +281,6 @@ "workbench-editor-resolver": {"assign": ["lramos15"]}, "workbench-editors": {"assign": ["bpasero"]}, "workbench-electron": {"assign": ["deepak1556"]}, - "workbench-feedback": {"assign": ["bpasero"]}, "workbench-fonts": {"assign": []}, "workbench-history": {"assign": ["bpasero"]}, "workbench-hot-exit": {"assign": ["bpasero"]}, diff --git a/.github/commands.json b/.github/commands.json index 200a47b18c0..15c6cd953c5 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -215,7 +215,7 @@ "action": "updateLabels", "addLabel": "info-needed", "removeLabel": "~confirmation-needed", - "comment": "Please perform the following **three tasks** to diagnose the root cause of the issue:\n\n* [ ] **1.) Disable Extensions**\n * Select `View` and pick `Command Palette...`\n * Run `Developer: Reload With Extensions Disabled`\n * šŸ‘‰ See if the issue reproduces\n\n* [ ] **2.) Disable Configuration**\n * Select `View` and pick `Command Palette...`\n * Run `Profiles: Create a Temporary Profile`\n * šŸ‘‰ See if the issue reproduces\n\n* [ ] **3.) Try VS Code Insiders**\n * Download [VS Code Insiders](https://code.visualstudio.com/insiders/)\n * Install and Run it\n * šŸ‘‰ See if the issue reproduces\n \nThen pick one of the three resolutions depending on which step has helped:\n\n
\n Disabling my Extensions helped\n\nPlease run the command `Start Extension Bisect` and follow the instructions to find the extension that is causing this issue.\n\nimage\n\nPlease report the issue to the extension causing this.\n
\n\n
\n Disabling my configuration helped\nPlease report back more details about your configuration, including settings.\n
\n\n
\n Using VS Code Insiders has helped\nāœ… This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use VS Code Insiders until the new stable version is available.\n
" + "comment": "Please diagnose the root cause of the issue by running the command `F1 > Help: Troubleshoot Issue` and following the instructions. Once you have done that, please update the issue with the results.\n\nHappy Coding!" }, { "type": "comment", diff --git a/.github/commands/codespaces_issue.yml b/.github/commands/codespaces_issue.yml new file mode 100644 index 00000000000..7abacafaf21 --- /dev/null +++ b/.github/commands/codespaces_issue.yml @@ -0,0 +1,11 @@ +# Learn more about the syntax here: +# https://docs.github.com/en/early-access/github/save-time-with-slash-commands/syntax-for-user-defined-slash-commands +--- +trigger: codespaces_issue +title: Codespaces Issue +description: Report downstream + +steps: + - type: fill + template: |- + This looks like an issue with the Codespaces service which we don't track in this repository. You can report this to the Codespaces team at https://github.com/orgs/community/discussions/categories/codespaces diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dac5f9e073b..8a4d93dfa77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: - name: Setup Build Environment run: | sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libkrb5-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb sudo chmod +x /etc/init.d/xvfb sudo update-rc.d xvfb defaults diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index c951476c083..2d90770bd25 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -47,6 +47,8 @@ jobs: with: configPath: classifier allowLabels: "info-needed|new release|error-telemetry|*english-please|translation-required" - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - manifestDbConnectionString: ${{secrets.MANIFEST_DB_CONNECTION_STRING}} + tenantId: ${{secrets.TOOLS_TENANT_ID}} + clientId: ${{secrets.TOOLS_CLIENT_ID}} + clientSecret: ${{secrets.TOOLS_CLIENT_SECRET}} + clientScope: ${{secrets.TOOLS_CLIENT_SCOPE}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} diff --git a/.github/workflows/monaco-editor.yml b/.github/workflows/monaco-editor.yml index a86f94bc331..46ece33df5c 100644 --- a/.github/workflows/monaco-editor.yml +++ b/.github/workflows/monaco-editor.yml @@ -45,6 +45,9 @@ jobs: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} restore-keys: ${{ runner.os }}-yarnCacheDir- + - name: Install libkrb5-dev + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + run: sudo apt install -y libkrb5-dev - name: Execute yarn if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} env: diff --git a/.github/workflows/no-yarn-lock-changes.yml b/.github/workflows/no-yarn-lock-changes.yml index ac32bc367dc..57082a28b1c 100644 --- a/.github/workflows/no-yarn-lock-changes.yml +++ b/.github/workflows/no-yarn-lock-changes.yml @@ -24,8 +24,8 @@ jobs: - name: Get file changes uses: trilom/file-changes-action@ce38c8ce2459ca3c303415eec8cb0409857b4272 if: ${{ steps.control.outputs.should_run == 'true' }} - - name: Check for yarn.lock changes + - name: Check for lockfile changes if: ${{ steps.control.outputs.should_run == 'true' }} run: | - cat $HOME/files.json | jq -e 'any(test("yarn\\.lock$")) | not' \ - || (echo "Changes to yarn.lock files aren't allowed in PRs." && exit 1) + cat $HOME/files.json | jq -e 'any(test("yarn\\.lock$|Cargo\\.lock$")) | not' \ + || (echo "Changes to yarn.lock/Cargo.lock files aren't allowed in PRs." && exit 1) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 682cd02911b..706cb4d5d38 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"June 2023\"" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"August 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index e0a1b766d76..842fe282f17 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"June 2023\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"August 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 74316c9367e..d41c81d839e 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"June 2023\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"August 2023\"\n\n$MINE=assignee:@me" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 9ca12439a70..06efd345018 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"June 2023\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"August 2023\"" }, { "kind": 1, @@ -102,7 +102,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:htmlfs -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keybindings-json -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-keybinding -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-search -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:terminal-winpty -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:tips-and-tricks -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:untitled-editor-hint -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom" + "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keybindings-json -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:terminal-winpty -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat" }, { "kind": 1, diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index f3c54eab361..1b81dcd0148 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -12,7 +12,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"June 2023\"" + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"August 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/vscode-dev.github-issues b/.vscode/notebooks/vscode-dev.github-issues index ffcd620be52..013c3331dca 100644 --- a/.vscode/notebooks/vscode-dev.github-issues +++ b/.vscode/notebooks/vscode-dev.github-issues @@ -2,7 +2,7 @@ { "kind": 2, "language": "github-issues", - "value": "$milestone=milestone:\"June 2023\"" + "value": "$milestone=milestone:\"August 2023\"" }, { "kind": 1, diff --git a/.vscode/settings.json b/.vscode/settings.json index 85e608eb901..7eefe0c57f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,6 +36,7 @@ "files.readonlyInclude": { "**/node_modules/**": true, "**/yarn.lock": true, + "**/Cargo.lock": true, "src/vs/workbench/workbench.web.main.css": true, "src/vs/workbench/workbench.desktop.main.css": true, "src/vs/workbench/workbench.desktop.main.nls.js": true, @@ -135,6 +136,7 @@ "git", "sash" ], + "githubPullRequests.experimental.createView": true, "debug.javascript.terminalOptions": { "outFiles": [ "${workspaceFolder}/out/**/*.js", diff --git a/.yarnrc b/.yarnrc index 3af2059dbd7..389a2bd709c 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "22.3.14" -ms_build_id "21893604" +target "22.3.18" +ms_build_id "22689846" runtime "electron" build_from_source "true" diff --git a/CodeQL.yml b/CodeQL.yml new file mode 100644 index 00000000000..eecb813a96f --- /dev/null +++ b/CodeQL.yml @@ -0,0 +1,29 @@ +path_classifiers: + test: + # Classify all files in the top-level directories test/ and testsuites/ as test code. + - test + # Classify all files with suffix `.test` as test code. + # Note: use only forward slash / as a path separator. + # * Matches any sequence of characters except a forward slash. + # ** Matches any sequence of characters, including a forward slash. + # This wildcard must either be surrounded by forward slash symbols, or used as the first segment of a path. + # It matches zero or more whole directory segments. There is no need to use a wildcard at the end of a directory path because all sub-directories are automatically matched. + # That is, /anything/ matches the anything directory and all its subdirectories. + # Always enclose the expression in double quotes if it includes *. + - "**/*.test.ts" + + # The default behavior is to tag all files created during the + # build as `generated`. Results are hidden for generated code. You can tag + # further files as being generated by adding them to the `generated` section. + generated: + # generated code. + - out + - "out-build" + - "out-vscode" + - "**/out/**" + + # The default behavior is to tag library code as `library`. Results are hidden + # for library code. You can tag further files as being library code by adding them + # to the `library` section. + library: + - "**/node_modules/**" diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index d5a4c5f2193..cf52ca35c94 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -169,45 +169,6 @@ OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -atom/language-java 0.32.1 - MIT -https://github.com/atom/language-java - -The MIT License (MIT) - -Copyright (c) 2014 GitHub Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -This package was derived from a TextMate bundle located at -https://github.com/textmate/java.tmbundle and distributed under the following -license, located in `README.mdown`: - -Permission to copy, use, modify, sell and distribute this -software is granted. This software is provided "as is" without -express or implied warranty, and with no claim as to its -suitability for any purpose. ---------------------------------------------------------- - ---------------------------------------------------------- - atom/language-sass 0.62.1 - MIT https://github.com/atom/language-sass @@ -493,17 +454,207 @@ b) the Mozilla Public License Version 2.0 ----------------------------------------------------------------------------- -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - http://www.apache.org/licenses/LICENSE-2.0 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. ----------------------------------------------------------------------------- Mozilla Public License, version 2.0 @@ -1275,7 +1426,7 @@ SOFTWARE. --------------------------------------------------------- -jeff-hykin/better-shell-syntax 1.5.4 - MIT +jeff-hykin/better-shell-syntax 1.6.2 - MIT https://github.com/jeff-hykin/better-shell-syntax MIT License @@ -1303,7 +1454,7 @@ SOFTWARE. --------------------------------------------------------- -jlelong/vscode-latex-basics 1.5.2 - MIT +jlelong/vscode-latex-basics 1.5.3 - MIT https://github.com/jlelong/vscode-latex-basics Copyright (c) vscode-latex-basics authors @@ -1986,6 +2137,46 @@ SOFTWARE. --------------------------------------------------------- +redhat-developer/vscode-java 1.21.0 - MIT +https://github.com/redhat-developer/vscode-java + +The MIT License (MIT) + +Copyright (c) 2014 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------- + +This package was derived from a TextMate bundle located at +https://github.com/textmate/java.tmbundle and distributed under the following +license, located in `README.mdown`: + +Permission to copy, use, modify, sell and distribute this +software is granted. This software is provided "as is" without +express or implied warranty, and with no claim as to its +suitability for any purpose. +--------------------------------------------------------- + +--------------------------------------------------------- + rust-syntax 0.5.0 - MIT https://github.com/dustypomerleau/rust-syntax diff --git a/build/.cachesalt b/build/.cachesalt index d63bdc31189..26ad5de2bca 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2023-06-12T12:55:48.130Z +2023-07-20T13:31:34.746Z diff --git a/build/.moduleignore b/build/.moduleignore index abc37e3138c..e4a19bbb762 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -73,6 +73,12 @@ windows-foreground-love/build/** windows-foreground-love/src/** !windows-foreground-love/**/*.node +kerberos/binding.gyp +kerberos/build/** +kerberos/src/** +kerberos/node_modules/** +!kerberos/**/*.node + keytar/binding.gyp keytar/build/** keytar/src/** @@ -108,14 +114,6 @@ vsda/SECURITY.md vsda/targets !vsda/build/Release/vsda.node -vscode-encrypt/build/** -vscode-encrypt/src/** -vscode-encrypt/vendor/** -vscode-encrypt/.gitignore -vscode-encrypt/binding.gyp -vscode-encrypt/README.md -!vscode-encrypt/build/Release/vscode-encrypt-native.node - @vscode/policy-watcher/build/** @vscode/policy-watcher/.husky/** @vscode/policy-watcher/src/** diff --git a/build/azure-pipelines/alpine/cli-build-alpine.yml b/build/azure-pipelines/alpine/cli-build-alpine.yml index 5ad6495cf97..8aa191023da 100644 --- a/build/azure-pipelines/alpine/cli-build-alpine.yml +++ b/build/azure-pipelines/alpine/cli-build-alpine.yml @@ -30,7 +30,7 @@ steps: workingDirectory: build displayName: Install pipeline build - - script: node build/azure-pipelines/distro/apply-cli-patches + - script: node .build/distro/cli-patches/index.js displayName: Apply distro patches - task: Npm@1 diff --git a/build/azure-pipelines/alpine/product-build-alpine.yml b/build/azure-pipelines/alpine/product-build-alpine.yml index 04f9ad2fcf2..44af6a76985 100644 --- a/build/azure-pipelines/alpine/product-build-alpine.yml +++ b/build/azure-pipelines/alpine/product-build-alpine.yml @@ -3,10 +3,6 @@ steps: inputs: versionSpec: "16.x" - - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - displayName: "Register Docker QEMU" - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - template: ../distro/download-distro.yml - task: AzureKeyVault@1 @@ -67,6 +63,10 @@ steps: displayName: "Pull image" condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: sudo apt-get update && sudo apt-get install -y libkrb5-dev + displayName: Install build dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: | set -e for i in {1..5}; do # try 5 times @@ -78,10 +78,12 @@ steps: echo "Yarn failed $i, trying again..." done env: + npm_config_arch: $(NPM_ARCH) ELECTRON_SKIP_BINARY_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 GITHUB_TOKEN: "$(github-distro-mixin-password)" VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:alpine-$(VSCODE_ARCH) + VSCODE_HOST_MOUNT: "/mnt/vss/_work/1/s" displayName: Install build dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) @@ -104,55 +106,51 @@ steps: - script: | set -e - TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") + TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") # TODO@joaomoreno yarn gulp vscode-reh-$TARGET-min-ci - yarn gulp vscode-reh-web-$TARGET-min-ci + (cd .. && mv vscode-reh-$TARGET vscode-server-$TARGET) # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/server/vscode-server-$TARGET.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-$TARGET + echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build + displayName: Build server - script: | set -e TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") - REPO="$(pwd)" - ROOT="$REPO/.." + yarn gulp vscode-reh-web-$TARGET-min-ci + (cd .. && mv vscode-reh-web-$TARGET vscode-server-$TARGET-web) # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/web/vscode-server-$TARGET-web.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-$TARGET-web + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Build server (web) - # Publish Remote Extension Host - LEGACY_SERVER_BUILD_NAME="vscode-reh-$TARGET" - SERVER_BUILD_NAME="vscode-server-$TARGET" - SERVER_TARBALL_FILENAME="vscode-server-$TARGET.tar.gz" - SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix - rm -rf $ROOT/vscode-server-*.tar.* - (cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - - # Publish Remote Extension Host (Web) - LEGACY_SERVER_BUILD_NAME="vscode-reh-web-$TARGET" - SERVER_BUILD_NAME="vscode-server-$TARGET-web" - SERVER_TARBALL_FILENAME="vscode-server-$TARGET-web.tar.gz" - SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - - rm -rf $ROOT/vscode-server-*-web.tar.* - (cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - displayName: Prepare for publish - - - publish: $(Agent.BuildDirectory)/vscode-server-alpine-$(VSCODE_ARCH).tar.gz - artifact: vscode_server_alpine_$(VSCODE_ARCH)_archive-unsigned + - publish: $(SERVER_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_server_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), ne(variables['VSCODE_ARCH'], 'x64')) - - publish: $(Agent.BuildDirectory)/vscode-server-alpine-$(VSCODE_ARCH)-web.tar.gz - artifact: vscode_web_alpine_$(VSCODE_ARCH)_archive-unsigned + - publish: $(WEB_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_web_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), ne(variables['VSCODE_ARCH'], 'x64')) # Legacy x64 artifact name - - publish: $(Agent.BuildDirectory)/vscode-server-linux-alpine.tar.gz - artifact: vscode_server_linux_alpine_archive-unsigned + - publish: $(SERVER_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_server_linux_alpine_archive-unsigned displayName: Publish x64 server archive - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), eq(variables['VSCODE_ARCH'], 'x64')) - - publish: $(Agent.BuildDirectory)/vscode-server-linux-alpine-web.tar.gz - artifact: vscode_web_linux_alpine_archive-unsigned + - publish: $(WEB_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_web_linux_alpine_archive-unsigned displayName: Publish x64 web server archive - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), eq(variables['VSCODE_ARCH'], 'x64')) diff --git a/build/azure-pipelines/cli/cli-apply-patches.yml b/build/azure-pipelines/cli/cli-apply-patches.yml index 5c5eb829c6b..35b429375c0 100644 --- a/build/azure-pipelines/cli/cli-apply-patches.yml +++ b/build/azure-pipelines/cli/cli-apply-patches.yml @@ -1,17 +1,5 @@ steps: - template: ../distro/download-distro.yml - - task: Cache@2 - inputs: - key: '"build_node_modules" | build/yarn.lock' - path: build/node_modules - cacheHitVar: BUILD_NODE_MODULES_RESTORED - displayName: Restore node_modules cache - - - script: yarn --frozen-lockfile --ignore-optional --check-files - workingDirectory: build - condition: and(succeeded(), ne(variables.BUILD_NODE_MODULES_RESTORED, 'true')) - displayName: Install pipeline build - - - script: node build/azure-pipelines/distro/apply-cli-patches + - script: node .build/distro/cli-patches/index.js displayName: Apply distro patches diff --git a/build/azure-pipelines/common/createAsset.js b/build/azure-pipelines/common/createAsset.js index c46745f351e..e4ca63b6573 100644 --- a/build/azure-pipelines/common/createAsset.js +++ b/build/azure-pipelines/common/createAsset.js @@ -167,10 +167,11 @@ async function main() { }; const uploadPromises = []; if (await blobClient.exists()) { - console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); + uploadPromises.push(Promise.reject(new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`))); } else { - uploadPromises.push((0, retry_1.retry)(async () => { + uploadPromises.push((0, retry_1.retry)(async (attempt) => { + console.log(`Uploading blobs to Azure storage (attempt ${attempt})...`); await blobClient.uploadFile(filePath, blobOptions); console.log('Blob successfully uploaded to Azure storage.'); })); @@ -182,25 +183,29 @@ async function main() { const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); if (await mooncakeBlobClient.exists()) { - console.log(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); + uploadPromises.push(Promise.reject(new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`))); } else { - uploadPromises.push((0, retry_1.retry)(async () => { + uploadPromises.push((0, retry_1.retry)(async (attempt) => { + console.log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); await mooncakeBlobClient.uploadFile(filePath, blobOptions); console.log('Blob successfully uploaded to Mooncake Azure storage.'); })); } - if (uploadPromises.length) { - console.log('Uploading blobs to Azure storage and Mooncake Azure storage...'); - } + } + const promiseResults = await Promise.allSettled(uploadPromises); + const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected'); + if (rejectedPromiseResults.length === 0) { + console.log('All blobs successfully uploaded.'); + } + else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { + console.warn(rejectedPromiseResults[0].reason.message); + console.log('Some blobs successfully uploaded.'); } else { - if (uploadPromises.length) { - console.log('Uploading blobs to Azure storage...'); - } + // eslint-disable-next-line no-throw-literal + throw rejectedPromiseResults[0]?.reason; } - await Promise.all(uploadPromises); - console.log(uploadPromises.length ? 'All blobs successfully uploaded.' : 'No blobs to upload.'); const assetUrl = `${process.env['AZURE_CDN_URL']}/${quality}/${blobName}`; const blobPath = new URL(assetUrl).pathname; const mooncakeUrl = `${process.env['MOONCAKE_CDN_URL']}${blobPath}`; @@ -230,4 +235,4 @@ main().then(() => { console.error(err); process.exit(1); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlQXNzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGVBc3NldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLHlCQUF5QjtBQUV6QixpQ0FBaUM7QUFDakMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG1DQUFnQztBQWFoQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtJQUM5QixPQUFPLENBQUMsS0FBSyxDQUFDLDJEQUEyRCxDQUFDLENBQUM7SUFDM0UsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0NBQ2pCO0FBRUQsd0ZBQXdGO0FBQ3hGLFNBQVMsV0FBVyxDQUFDLE9BQWUsRUFBRSxFQUFVLEVBQUUsSUFBWSxFQUFFLElBQVk7SUFDM0UsUUFBUSxFQUFFLEVBQUU7UUFDWCxLQUFLLE9BQU87WUFDWCxRQUFRLE9BQU8sRUFBRTtnQkFDaEIsS0FBSyxRQUFRLENBQUMsQ0FBQztvQkFDZCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUM7b0JBQzFELFFBQVEsSUFBSSxFQUFFO3dCQUNiLEtBQUssU0FBUzs0QkFDYixPQUFPLEdBQUcsS0FBSyxVQUFVLENBQUM7d0JBQzNCLEtBQUssT0FBTzs0QkFDWCxPQUFPLEtBQUssQ0FBQzt3QkFDZCxLQUFLLFlBQVk7NEJBQ2hCLE9BQU8sR0FBRyxLQUFLLE9BQU8sQ0FBQzt3QkFDeEI7NEJBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbkU7aUJBQ0Q7Z0JBQ0QsS0FBSyxRQUFRO29CQUNaLElBQUksSUFBSSxLQUFLLE9BQU8sRUFBRTt3QkFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbEU7b0JBQ0QsT0FBTyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixJQUFJLEVBQUUsQ0FBQztnQkFDbEUsS0FBSyxLQUFLO29CQUNULElBQUksSUFBSSxLQUFLLE9BQU8sRUFBRTt3QkFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbEU7b0JBQ0QsT0FBTyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLElBQUksTUFBTSxDQUFDO2dCQUMxRSxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxhQUFhLElBQUksRUFBRSxDQUFDO2dCQUM1QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ25FO1FBQ0YsS0FBSyxRQUFRO1lBQ1osUUFBUSxPQUFPLEVBQUU7Z0JBQ2hCLEtBQUssUUFBUTtvQkFDWixPQUFPLGlCQUFpQixJQUFJLEVBQUUsQ0FBQztnQkFDaEMsS0FBSyxLQUFLO29CQUNULE9BQU8saUJBQWlCLElBQUksTUFBTSxDQUFDO2dCQUNwQyxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxjQUFjLElBQUksRUFBRSxDQUFDO2dCQUM3QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ25FO1FBQ0YsS0FBSyxPQUFPO1lBQ1gsUUFBUSxJQUFJLEVBQUU7Z0JBQ2IsS0FBSyxNQUFNO29CQUNWLE9BQU8sY0FBYyxJQUFJLEVBQUUsQ0FBQztnQkFDN0IsS0FBSyxrQkFBa0I7b0JBQ3RCLFFBQVEsT0FBTyxFQUFFO3dCQUNoQixLQUFLLFFBQVE7NEJBQ1osT0FBTyxTQUFTLElBQUksRUFBRSxDQUFDO3dCQUN4QixLQUFLLFFBQVE7NEJBQ1osT0FBTyxnQkFBZ0IsSUFBSSxFQUFFLENBQUM7d0JBQy9CLEtBQUssS0FBSzs0QkFDVCxPQUFPLElBQUksS0FBSyxZQUFZLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsSUFBSSxNQUFNLENBQUM7d0JBQzlFOzRCQUNDLE1BQU0sSUFBSSxLQUFLLENBQUMsaUJBQWlCLE9BQU8sSUFBSSxFQUFFLElBQUksSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDLENBQUM7cUJBQ25FO2dCQUNGLEtBQUssYUFBYTtvQkFDakIsT0FBTyxhQUFhLElBQUksRUFBRSxDQUFDO2dCQUM1QixLQUFLLGFBQWE7b0JBQ2pCLE9BQU8sYUFBYSxJQUFJLEVBQUUsQ0FBQztnQkFDNUIsS0FBSyxLQUFLO29CQUNULE9BQU8sYUFBYSxJQUFJLEVBQUUsQ0FBQztnQkFDNUI7b0JBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQzthQUNuRTtRQUNGLEtBQUssUUFBUTtZQUNaLFFBQVEsT0FBTyxFQUFFO2dCQUNoQixLQUFLLFFBQVE7b0JBQ1osSUFBSSxJQUFJLEtBQUssS0FBSyxFQUFFO3dCQUNuQixPQUFPLFFBQVEsQ0FBQztxQkFDaEI7b0JBQ0QsT0FBTyxVQUFVLElBQUksRUFBRSxDQUFDO2dCQUN6QixLQUFLLFFBQVE7b0JBQ1osSUFBSSxJQUFJLEtBQUssS0FBSyxFQUFFO3dCQUNuQixPQUFPLGVBQWUsQ0FBQztxQkFDdkI7b0JBQ0QsT0FBTyxpQkFBaUIsSUFBSSxFQUFFLENBQUM7Z0JBQ2hDLEtBQUssS0FBSztvQkFDVCxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUU7d0JBQ25CLE9BQU8sbUJBQW1CLENBQUM7cUJBQzNCO29CQUNELE9BQU8saUJBQWlCLElBQUksTUFBTSxDQUFDO2dCQUNwQyxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxjQUFjLElBQUksRUFBRSxDQUFDO2dCQUM3QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ25FO1FBQ0Y7WUFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0tBQ25FO0FBQ0YsQ0FBQztBQUVELDhFQUE4RTtBQUM5RSxTQUFTLFdBQVcsQ0FBQyxJQUFZO0lBQ2hDLFFBQVEsSUFBSSxFQUFFO1FBQ2IsS0FBSyxZQUFZO1lBQ2hCLE9BQU8sT0FBTyxDQUFDO1FBQ2hCLEtBQUssYUFBYSxDQUFDO1FBQ25CLEtBQUssYUFBYTtZQUNqQixPQUFPLFNBQVMsQ0FBQztRQUNsQjtZQUNDLE9BQU8sSUFBSSxDQUFDO0tBQ2I7QUFDRixDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsUUFBZ0IsRUFBRSxNQUFnQjtJQUNyRCxPQUFPLElBQUksT0FBTyxDQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ25DLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFM0MsTUFBTTthQUNKLEVBQUUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7YUFDdEMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7YUFDZCxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5QyxDQUFDLENBQUMsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFTLE1BQU0sQ0FBQyxJQUFZO0lBQzNCLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFFakMsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLEVBQUU7UUFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLENBQUM7S0FDeEM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNmLENBQUM7QUFFRCxLQUFLLFVBQVUsSUFBSTtJQUNsQixNQUFNLENBQUMsRUFBRSxBQUFELEVBQUcsT0FBTyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsZUFBZSxFQUFFLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO0lBQ2xGLHdDQUF3QztJQUN4QyxNQUFNLFFBQVEsR0FBRyxXQUFXLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDakUsTUFBTSxJQUFJLEdBQUcsV0FBVyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3pDLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO0lBRTdDLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLENBQUMsQ0FBQztJQUVqQyxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksT0FBTyxDQUFXLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3RyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBRXZCLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBRTNCLE1BQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUM3QyxNQUFNLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEVBQUUsVUFBVSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFN0csT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDL0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFFbkMsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLEdBQUcsR0FBRyxRQUFRLENBQUM7SUFFekMsTUFBTSxzQkFBc0IsR0FBMkIsRUFBRSxZQUFZLEVBQUUsRUFBRSxlQUFlLEVBQUUscUNBQXNCLENBQUMsV0FBVyxFQUFFLFFBQVEsRUFBRSxDQUFDLEVBQUUsY0FBYyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLEVBQUUsQ0FBQztJQUU5SyxNQUFNLFVBQVUsR0FBRyxJQUFJLGlDQUFzQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBRSxDQUFDLENBQUM7SUFDckosTUFBTSxpQkFBaUIsR0FBRyxJQUFJLGdDQUFpQixDQUFDLHNDQUFzQyxFQUFFLFVBQVUsRUFBRSxzQkFBc0IsQ0FBQyxDQUFDO0lBQzVILE1BQU0sZUFBZSxHQUFHLGlCQUFpQixDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sVUFBVSxHQUFHLGVBQWUsQ0FBQyxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUVoRSxNQUFNLFdBQVcsR0FBbUM7UUFDbkQsZUFBZSxFQUFFO1lBQ2hCLGVBQWUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQztZQUN0QyxzQkFBc0IsRUFBRSx5QkFBeUIsUUFBUSxHQUFHO1lBQzVELGdCQUFnQixFQUFFLDBCQUEwQjtTQUM1QztLQUNELENBQUM7SUFFRixNQUFNLGNBQWMsR0FBb0IsRUFBRSxDQUFDO0lBQzNDLElBQUksTUFBTSxVQUFVLENBQUMsTUFBTSxFQUFFLEVBQUU7UUFDOUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLE9BQU8sS0FBSyxRQUFRLHdDQUF3QyxDQUFDLENBQUM7S0FDbEY7U0FBTTtRQUNOLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBQSxhQUFLLEVBQUMsS0FBSyxJQUFJLEVBQUU7WUFDcEMsTUFBTSxVQUFVLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztZQUNuRCxPQUFPLENBQUMsR0FBRyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7UUFDN0QsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUNKO0lBRUQsTUFBTSxzQkFBc0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQztJQUVqRyxJQUFJLHNCQUFzQixFQUFFO1FBQzNCLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxpQ0FBc0IsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDBCQUEwQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsQ0FBRSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsOEJBQThCLENBQUUsQ0FBQyxDQUFDO1FBQ3hMLE1BQU0seUJBQXlCLEdBQUcsSUFBSSxnQ0FBaUIsQ0FBQywyQ0FBMkMsRUFBRSxrQkFBa0IsRUFBRSxzQkFBc0IsQ0FBQyxDQUFDO1FBQ2pKLE1BQU0sdUJBQXVCLEdBQUcseUJBQXlCLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDdEYsTUFBTSxrQkFBa0IsR0FBRyx1QkFBdUIsQ0FBQyxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUVoRixJQUFJLE1BQU0sa0JBQWtCLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsT0FBTyxLQUFLLFFBQVEsd0NBQXdDLENBQUMsQ0FBQztTQUMzRjthQUFNO1lBQ04sY0FBYyxDQUFDLElBQUksQ0FBQyxJQUFBLGFBQUssRUFBQyxLQUFLLElBQUksRUFBRTtnQkFDcEMsTUFBTSxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsUUFBUSxFQUFFLFdBQVcsQ0FBQyxDQUFDO2dCQUMzRCxPQUFPLENBQUMsR0FBRyxDQUFDLHVEQUF1RCxDQUFDLENBQUM7WUFDdEUsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNKO1FBRUQsSUFBSSxjQUFjLENBQUMsTUFBTSxFQUFFO1lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0VBQWdFLENBQUMsQ0FBQztTQUM5RTtLQUNEO1NBQU07UUFDTixJQUFJLGNBQWMsQ0FBQyxNQUFNLEVBQUU7WUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFDO1NBQ25EO0tBQ0Q7SUFFRCxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7SUFFbEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDLENBQUMscUJBQXFCLENBQUMsQ0FBQztJQUVoRyxNQUFNLFFBQVEsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLElBQUksT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBQzFFLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDLFFBQVEsQ0FBQztJQUM1QyxNQUFNLFdBQVcsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsa0JBQWtCLENBQUMsR0FBRyxRQUFRLEVBQUUsQ0FBQztJQUVwRSxNQUFNLEtBQUssR0FBVTtRQUNwQixRQUFRO1FBQ1IsSUFBSTtRQUNKLEdBQUcsRUFBRSxRQUFRO1FBQ2IsSUFBSSxFQUFFLFFBQVE7UUFDZCxXQUFXO1FBQ1gsVUFBVTtRQUNWLElBQUk7S0FDSixDQUFDO0lBRUYsbUVBQW1FO0lBQ25FLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRTtRQUMzQixLQUFLLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDO0tBQ2hDO0lBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFFekQsTUFBTSxNQUFNLEdBQUcsSUFBSSxxQkFBWSxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUUsRUFBRSxjQUFjLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUNySCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFDckUsTUFBTSxJQUFBLGFBQUssRUFBQyxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLGFBQWEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUU3RixPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBQzFCLENBQUM7QUFFRCxJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO0lBQ2hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztJQUMxQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2pCLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRTtJQUNSLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDbkIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNqQixDQUFDLENBQUMsQ0FBQyJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlQXNzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGVBc3NldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLHlCQUF5QjtBQUV6QixpQ0FBaUM7QUFDakMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG1DQUFnQztBQWFoQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtJQUM5QixPQUFPLENBQUMsS0FBSyxDQUFDLDJEQUEyRCxDQUFDLENBQUM7SUFDM0UsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0NBQ2pCO0FBRUQsd0ZBQXdGO0FBQ3hGLFNBQVMsV0FBVyxDQUFDLE9BQWUsRUFBRSxFQUFVLEVBQUUsSUFBWSxFQUFFLElBQVk7SUFDM0UsUUFBUSxFQUFFLEVBQUU7UUFDWCxLQUFLLE9BQU87WUFDWCxRQUFRLE9BQU8sRUFBRTtnQkFDaEIsS0FBSyxRQUFRLENBQUMsQ0FBQztvQkFDZCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUM7b0JBQzFELFFBQVEsSUFBSSxFQUFFO3dCQUNiLEtBQUssU0FBUzs0QkFDYixPQUFPLEdBQUcsS0FBSyxVQUFVLENBQUM7d0JBQzNCLEtBQUssT0FBTzs0QkFDWCxPQUFPLEtBQUssQ0FBQzt3QkFDZCxLQUFLLFlBQVk7NEJBQ2hCLE9BQU8sR0FBRyxLQUFLLE9BQU8sQ0FBQzt3QkFDeEI7NEJBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbkU7aUJBQ0Q7Z0JBQ0QsS0FBSyxRQUFRO29CQUNaLElBQUksSUFBSSxLQUFLLE9BQU8sRUFBRTt3QkFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbEU7b0JBQ0QsT0FBTyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixJQUFJLEVBQUUsQ0FBQztnQkFDbEUsS0FBSyxLQUFLO29CQUNULElBQUksSUFBSSxLQUFLLE9BQU8sRUFBRTt3QkFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbEU7b0JBQ0QsT0FBTyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLElBQUksTUFBTSxDQUFDO2dCQUMxRSxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxhQUFhLElBQUksRUFBRSxDQUFDO2dCQUM1QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ25FO1FBQ0YsS0FBSyxRQUFRO1lBQ1osUUFBUSxPQUFPLEVBQUU7Z0JBQ2hCLEtBQUssUUFBUTtvQkFDWixPQUFPLGlCQUFpQixJQUFJLEVBQUUsQ0FBQztnQkFDaEMsS0FBSyxLQUFLO29CQUNULE9BQU8saUJBQWlCLElBQUksTUFBTSxDQUFDO2dCQUNwQyxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxjQUFjLElBQUksRUFBRSxDQUFDO2dCQUM3QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ25FO1FBQ0YsS0FBSyxPQUFPO1lBQ1gsUUFBUSxJQUFJLEVBQUU7Z0JBQ2IsS0FBSyxNQUFNO29CQUNWLE9BQU8sY0FBYyxJQUFJLEVBQUUsQ0FBQztnQkFDN0IsS0FBSyxrQkFBa0I7b0JBQ3RCLFFBQVEsT0FBTyxFQUFFO3dCQUNoQixLQUFLLFFBQVE7NEJBQ1osT0FBTyxTQUFTLElBQUksRUFBRSxDQUFDO3dCQUN4QixLQUFLLFFBQVE7NEJBQ1osT0FBTyxnQkFBZ0IsSUFBSSxFQUFFLENBQUM7d0JBQy9CLEtBQUssS0FBSzs0QkFDVCxPQUFPLElBQUksS0FBSyxZQUFZLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsSUFBSSxNQUFNLENBQUM7d0JBQzlFOzRCQUNDLE1BQU0sSUFBSSxLQUFLLENBQUMsaUJBQWlCLE9BQU8sSUFBSSxFQUFFLElBQUksSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDLENBQUM7cUJBQ25FO2dCQUNGLEtBQUssYUFBYTtvQkFDakIsT0FBTyxhQUFhLElBQUksRUFBRSxDQUFDO2dCQUM1QixLQUFLLGFBQWE7b0JBQ2pCLE9BQU8sYUFBYSxJQUFJLEVBQUUsQ0FBQztnQkFDNUIsS0FBSyxLQUFLO29CQUNULE9BQU8sYUFBYSxJQUFJLEVBQUUsQ0FBQztnQkFDNUI7b0JBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQzthQUNuRTtRQUNGLEtBQUssUUFBUTtZQUNaLFFBQVEsT0FBTyxFQUFFO2dCQUNoQixLQUFLLFFBQVE7b0JBQ1osSUFBSSxJQUFJLEtBQUssS0FBSyxFQUFFO3dCQUNuQixPQUFPLFFBQVEsQ0FBQztxQkFDaEI7b0JBQ0QsT0FBTyxVQUFVLElBQUksRUFBRSxDQUFDO2dCQUN6QixLQUFLLFFBQVE7b0JBQ1osSUFBSSxJQUFJLEtBQUssS0FBSyxFQUFFO3dCQUNuQixPQUFPLGVBQWUsQ0FBQztxQkFDdkI7b0JBQ0QsT0FBTyxpQkFBaUIsSUFBSSxFQUFFLENBQUM7Z0JBQ2hDLEtBQUssS0FBSztvQkFDVCxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUU7d0JBQ25CLE9BQU8sbUJBQW1CLENBQUM7cUJBQzNCO29CQUNELE9BQU8saUJBQWlCLElBQUksTUFBTSxDQUFDO2dCQUNwQyxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxjQUFjLElBQUksRUFBRSxDQUFDO2dCQUM3QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2FBQ25FO1FBQ0Y7WUFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0tBQ25FO0FBQ0YsQ0FBQztBQUVELDhFQUE4RTtBQUM5RSxTQUFTLFdBQVcsQ0FBQyxJQUFZO0lBQ2hDLFFBQVEsSUFBSSxFQUFFO1FBQ2IsS0FBSyxZQUFZO1lBQ2hCLE9BQU8sT0FBTyxDQUFDO1FBQ2hCLEtBQUssYUFBYSxDQUFDO1FBQ25CLEtBQUssYUFBYTtZQUNqQixPQUFPLFNBQVMsQ0FBQztRQUNsQjtZQUNDLE9BQU8sSUFBSSxDQUFDO0tBQ2I7QUFDRixDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsUUFBZ0IsRUFBRSxNQUFnQjtJQUNyRCxPQUFPLElBQUksT0FBTyxDQUFTLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ25DLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFM0MsTUFBTTthQUNKLEVBQUUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7YUFDdEMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7YUFDZCxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5QyxDQUFDLENBQUMsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFTLE1BQU0sQ0FBQyxJQUFZO0lBQzNCLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFFakMsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLEVBQUU7UUFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLENBQUM7S0FDeEM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNmLENBQUM7QUFFRCxLQUFLLFVBQVUsSUFBSTtJQUNsQixNQUFNLENBQUMsRUFBRSxBQUFELEVBQUcsT0FBTyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsZUFBZSxFQUFFLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO0lBQ2xGLHdDQUF3QztJQUN4QyxNQUFNLFFBQVEsR0FBRyxXQUFXLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDakUsTUFBTSxJQUFJLEdBQUcsV0FBVyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3pDLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO0lBRTdDLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLENBQUMsQ0FBQztJQUVqQyxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksT0FBTyxDQUFXLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3RyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBRXZCLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBRTNCLE1BQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUM3QyxNQUFNLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEVBQUUsVUFBVSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFN0csT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDL0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFFbkMsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLEdBQUcsR0FBRyxRQUFRLENBQUM7SUFFekMsTUFBTSxzQkFBc0IsR0FBMkIsRUFBRSxZQUFZLEVBQUUsRUFBRSxlQUFlLEVBQUUscUNBQXNCLENBQUMsV0FBVyxFQUFFLFFBQVEsRUFBRSxDQUFDLEVBQUUsY0FBYyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLEVBQUUsQ0FBQztJQUU5SyxNQUFNLFVBQVUsR0FBRyxJQUFJLGlDQUFzQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBRSxDQUFDLENBQUM7SUFDckosTUFBTSxpQkFBaUIsR0FBRyxJQUFJLGdDQUFpQixDQUFDLHNDQUFzQyxFQUFFLFVBQVUsRUFBRSxzQkFBc0IsQ0FBQyxDQUFDO0lBQzVILE1BQU0sZUFBZSxHQUFHLGlCQUFpQixDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sVUFBVSxHQUFHLGVBQWUsQ0FBQyxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUVoRSxNQUFNLFdBQVcsR0FBbUM7UUFDbkQsZUFBZSxFQUFFO1lBQ2hCLGVBQWUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQztZQUN0QyxzQkFBc0IsRUFBRSx5QkFBeUIsUUFBUSxHQUFHO1lBQzVELGdCQUFnQixFQUFFLDBCQUEwQjtTQUM1QztLQUNELENBQUM7SUFFRixNQUFNLGNBQWMsR0FBb0IsRUFBRSxDQUFDO0lBRTNDLElBQUksTUFBTSxVQUFVLENBQUMsTUFBTSxFQUFFLEVBQUU7UUFDOUIsY0FBYyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLFFBQVEsT0FBTyxLQUFLLFFBQVEsd0NBQXdDLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDckg7U0FBTTtRQUNOLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBQSxhQUFLLEVBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO1lBQzNDLE9BQU8sQ0FBQyxHQUFHLENBQUMsNkNBQTZDLE9BQU8sTUFBTSxDQUFDLENBQUM7WUFDeEUsTUFBTSxVQUFVLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztZQUNuRCxPQUFPLENBQUMsR0FBRyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7UUFDN0QsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUNKO0lBRUQsTUFBTSxzQkFBc0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQztJQUVqRyxJQUFJLHNCQUFzQixFQUFFO1FBQzNCLE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxpQ0FBc0IsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDBCQUEwQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsQ0FBRSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsOEJBQThCLENBQUUsQ0FBQyxDQUFDO1FBQ3hMLE1BQU0seUJBQXlCLEdBQUcsSUFBSSxnQ0FBaUIsQ0FBQywyQ0FBMkMsRUFBRSxrQkFBa0IsRUFBRSxzQkFBc0IsQ0FBQyxDQUFDO1FBQ2pKLE1BQU0sdUJBQXVCLEdBQUcseUJBQXlCLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDdEYsTUFBTSxrQkFBa0IsR0FBRyx1QkFBdUIsQ0FBQyxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUVoRixJQUFJLE1BQU0sa0JBQWtCLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDdEMsY0FBYyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLEtBQUssUUFBUSx3Q0FBd0MsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUM5SDthQUFNO1lBQ04sY0FBYyxDQUFDLElBQUksQ0FBQyxJQUFBLGFBQUssRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7Z0JBQzNDLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0RBQXNELE9BQU8sTUFBTSxDQUFDLENBQUM7Z0JBQ2pGLE1BQU0sa0JBQWtCLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztnQkFDM0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1lBQ3RFLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDSjtLQUNEO0lBRUQsTUFBTSxjQUFjLEdBQUcsTUFBTSxPQUFPLENBQUMsVUFBVSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQ2hFLE1BQU0sc0JBQXNCLEdBQUcsY0FBYyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEtBQUssVUFBVSxDQUE0QixDQUFDO0lBRXhILElBQUksc0JBQXNCLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUN4QyxPQUFPLENBQUMsR0FBRyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7S0FDaEQ7U0FBTSxJQUFJLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxDQUFDLGdCQUFnQixDQUFDLEVBQUU7UUFDbEYsT0FBTyxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDdkQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDO0tBQ2pEO1NBQU07UUFDTiw0Q0FBNEM7UUFDNUMsTUFBTSxzQkFBc0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUM7S0FDeEM7SUFFRCxNQUFNLFFBQVEsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLElBQUksT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBQzFFLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDLFFBQVEsQ0FBQztJQUM1QyxNQUFNLFdBQVcsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsa0JBQWtCLENBQUMsR0FBRyxRQUFRLEVBQUUsQ0FBQztJQUVwRSxNQUFNLEtBQUssR0FBVTtRQUNwQixRQUFRO1FBQ1IsSUFBSTtRQUNKLEdBQUcsRUFBRSxRQUFRO1FBQ2IsSUFBSSxFQUFFLFFBQVE7UUFDZCxXQUFXO1FBQ1gsVUFBVTtRQUNWLElBQUk7S0FDSixDQUFDO0lBRUYsbUVBQW1FO0lBQ25FLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRTtRQUMzQixLQUFLLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDO0tBQ2hDO0lBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFFekQsTUFBTSxNQUFNLEdBQUcsSUFBSSxxQkFBWSxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUUsRUFBRSxjQUFjLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUNySCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFDckUsTUFBTSxJQUFBLGFBQUssRUFBQyxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLGFBQWEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUU3RixPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBQzFCLENBQUM7QUFFRCxJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO0lBQ2hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztJQUMxQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2pCLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRTtJQUNSLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDbkIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNqQixDQUFDLENBQUMsQ0FBQyJ9 \ No newline at end of file diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index 2f3f27c1d5b..74c76407a78 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -196,10 +196,12 @@ async function main(): Promise { }; const uploadPromises: Promise[] = []; + if (await blobClient.exists()) { - console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); + uploadPromises.push(Promise.reject(new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`))); } else { - uploadPromises.push(retry(async () => { + uploadPromises.push(retry(async (attempt) => { + console.log(`Uploading blobs to Azure storage (attempt ${attempt})...`); await blobClient.uploadFile(filePath, blobOptions); console.log('Blob successfully uploaded to Azure storage.'); })); @@ -214,26 +216,28 @@ async function main(): Promise { const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); if (await mooncakeBlobClient.exists()) { - console.log(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); + uploadPromises.push(Promise.reject(new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`))); } else { - uploadPromises.push(retry(async () => { + uploadPromises.push(retry(async (attempt) => { + console.log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); await mooncakeBlobClient.uploadFile(filePath, blobOptions); console.log('Blob successfully uploaded to Mooncake Azure storage.'); })); } - - if (uploadPromises.length) { - console.log('Uploading blobs to Azure storage and Mooncake Azure storage...'); - } - } else { - if (uploadPromises.length) { - console.log('Uploading blobs to Azure storage...'); - } } - await Promise.all(uploadPromises); + const promiseResults = await Promise.allSettled(uploadPromises); + const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected') as PromiseRejectedResult[]; - console.log(uploadPromises.length ? 'All blobs successfully uploaded.' : 'No blobs to upload.'); + if (rejectedPromiseResults.length === 0) { + console.log('All blobs successfully uploaded.'); + } else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { + console.warn(rejectedPromiseResults[0].reason.message); + console.log('Some blobs successfully uploaded.'); + } else { + // eslint-disable-next-line no-throw-literal + throw rejectedPromiseResults[0]?.reason; + } const assetUrl = `${process.env['AZURE_CDN_URL']}/${quality}/${blobName}`; const blobPath = new URL(assetUrl).pathname; diff --git a/build/azure-pipelines/common/releaseBuild.js b/build/azure-pipelines/common/releaseBuild.js index 5ba4e218ae0..752cb4db92c 100644 --- a/build/azure-pipelines/common/releaseBuild.js +++ b/build/azure-pipelines/common/releaseBuild.js @@ -46,11 +46,12 @@ async function main(force) { await (0, retry_1.retry)(() => scripts.storedProcedure('releaseBuild').execute('', [commit])); } const [, , force] = process.argv; -main(force === 'true').then(() => { +console.log(process.argv); +main(/^true$/i.test(force)).then(() => { console.log('Build successfully released'); process.exit(0); }, err => { console.error(err); process.exit(1); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVsZWFzZUJ1aWxkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmVsZWFzZUJ1aWxkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7QUFFaEcsOENBQXlEO0FBQ3pELDBDQUE2QztBQUM3QyxtQ0FBZ0M7QUFFaEMsU0FBUyxNQUFNLENBQUMsSUFBWTtJQUMzQixNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRWpDLElBQUksT0FBTyxNQUFNLEtBQUssV0FBVyxFQUFFO1FBQ2xDLE1BQU0sSUFBSSxLQUFLLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxDQUFDO0tBQ3hDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDZixDQUFDO0FBT0QsU0FBUyxtQkFBbUIsQ0FBQyxPQUFlO0lBQzNDLE9BQU87UUFDTixFQUFFLEVBQUUsT0FBTztRQUNYLE1BQU0sRUFBRSxLQUFLO0tBQ2IsQ0FBQztBQUNILENBQUM7QUFFRCxLQUFLLFVBQVUsU0FBUyxDQUFDLE1BQW9CLEVBQUUsT0FBZTtJQUM3RCxNQUFNLEtBQUssR0FBRyx1Q0FBdUMsT0FBTyxHQUFHLENBQUM7SUFFaEUsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRTlGLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQy9CLE9BQU8sbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDcEM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFXLENBQUM7QUFDbkMsQ0FBQztBQUVELEtBQUssVUFBVSxJQUFJLENBQUMsS0FBYztJQUNqQyxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMscUJBQXFCLENBQUMsQ0FBQztJQUM3QyxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUV6QyxNQUFNLGNBQWMsR0FBRyxJQUFJLGlDQUFzQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBRSxDQUFDLENBQUM7SUFDekosTUFBTSxNQUFNLEdBQUcsSUFBSSxxQkFBWSxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUUsRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFDO0lBRXpHLElBQUksQ0FBQyxLQUFLLEVBQUU7UUFDWCxNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFaEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUV2QyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUU7WUFDbEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsT0FBTyxhQUFhLENBQUMsQ0FBQztZQUN0RSxPQUFPO1NBQ1A7S0FDRDtJQUVELE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLE1BQU0sS0FBSyxDQUFDLENBQUM7SUFFNUMsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDO0lBQ3JFLE1BQU0sSUFBQSxhQUFLLEVBQUMsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2xGLENBQUM7QUFFRCxNQUFNLENBQUMsRUFBRSxBQUFELEVBQUcsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztBQUVqQyxJQUFJLENBQUMsS0FBSyxLQUFLLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7SUFDaEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBQzNDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakIsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFFO0lBQ1IsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2pCLENBQUMsQ0FBQyxDQUFDIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVsZWFzZUJ1aWxkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmVsZWFzZUJ1aWxkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7QUFFaEcsOENBQXlEO0FBQ3pELDBDQUE2QztBQUM3QyxtQ0FBZ0M7QUFFaEMsU0FBUyxNQUFNLENBQUMsSUFBWTtJQUMzQixNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRWpDLElBQUksT0FBTyxNQUFNLEtBQUssV0FBVyxFQUFFO1FBQ2xDLE1BQU0sSUFBSSxLQUFLLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxDQUFDO0tBQ3hDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDZixDQUFDO0FBT0QsU0FBUyxtQkFBbUIsQ0FBQyxPQUFlO0lBQzNDLE9BQU87UUFDTixFQUFFLEVBQUUsT0FBTztRQUNYLE1BQU0sRUFBRSxLQUFLO0tBQ2IsQ0FBQztBQUNILENBQUM7QUFFRCxLQUFLLFVBQVUsU0FBUyxDQUFDLE1BQW9CLEVBQUUsT0FBZTtJQUM3RCxNQUFNLEtBQUssR0FBRyx1Q0FBdUMsT0FBTyxHQUFHLENBQUM7SUFFaEUsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRTlGLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQy9CLE9BQU8sbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDcEM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFXLENBQUM7QUFDbkMsQ0FBQztBQUVELEtBQUssVUFBVSxJQUFJLENBQUMsS0FBYztJQUNqQyxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMscUJBQXFCLENBQUMsQ0FBQztJQUM3QyxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUV6QyxNQUFNLGNBQWMsR0FBRyxJQUFJLGlDQUFzQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBRSxDQUFDLENBQUM7SUFDekosTUFBTSxNQUFNLEdBQUcsSUFBSSxxQkFBWSxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLENBQUUsRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFDO0lBRXpHLElBQUksQ0FBQyxLQUFLLEVBQUU7UUFDWCxNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFaEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUV2QyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEVBQUU7WUFDbEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsT0FBTyxhQUFhLENBQUMsQ0FBQztZQUN0RSxPQUFPO1NBQ1A7S0FDRDtJQUVELE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLE1BQU0sS0FBSyxDQUFDLENBQUM7SUFFNUMsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDO0lBQ3JFLE1BQU0sSUFBQSxhQUFLLEVBQUMsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2xGLENBQUM7QUFFRCxNQUFNLENBQUMsRUFBRSxBQUFELEVBQUcsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztBQUVqQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUUxQixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7SUFDckMsT0FBTyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBQzNDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakIsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFFO0lBQ1IsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2pCLENBQUMsQ0FBQyxDQUFDIn0= \ No newline at end of file diff --git a/build/azure-pipelines/common/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index fda389f3455..2e8fff04fb4 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -67,7 +67,9 @@ async function main(force: boolean): Promise { const [, , force] = process.argv; -main(force === 'true').then(() => { +console.log(process.argv); + +main(/^true$/i.test(force)).then(() => { console.log('Build successfully released'); process.exit(0); }, err => { diff --git a/build/azure-pipelines/common/retry.js b/build/azure-pipelines/common/retry.js index 5330d540ced..657f1a5b08d 100644 --- a/build/azure-pipelines/common/retry.js +++ b/build/azure-pipelines/common/retry.js @@ -9,7 +9,7 @@ async function retry(fn) { let lastError; for (let run = 1; run <= 10; run++) { try { - return await fn(); + return await fn(run); } catch (err) { if (!/ECONNRESET|CredentialUnavailableError|Audience validation failed/i.test(err.message)) { @@ -26,4 +26,4 @@ async function retry(fn) { throw lastError; } exports.retry = retry; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJyZXRyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUV6RixLQUFLLFVBQVUsS0FBSyxDQUFJLEVBQW9CO0lBQ2xELElBQUksU0FBNEIsQ0FBQztJQUVqQyxLQUFLLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFO1FBQ25DLElBQUk7WUFDSCxPQUFPLE1BQU0sRUFBRSxFQUFFLENBQUM7U0FDbEI7UUFBQyxPQUFPLEdBQUcsRUFBRTtZQUNiLElBQUksQ0FBQyxtRUFBbUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUMzRixNQUFNLEdBQUcsQ0FBQzthQUNWO1lBRUQsU0FBUyxHQUFHLEdBQUcsQ0FBQztZQUNoQixNQUFNLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ2pFLE9BQU8sQ0FBQyxHQUFHLENBQUMsK0JBQStCLE1BQU0sT0FBTyxDQUFDLENBQUM7WUFFMUQsMENBQTBDO1lBQzFDLE1BQU0sSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7U0FDOUM7S0FDRDtJQUVELE9BQU8sQ0FBQyxHQUFHLENBQUMsNkJBQTZCLENBQUMsQ0FBQztJQUMzQyxNQUFNLFNBQVMsQ0FBQztBQUNqQixDQUFDO0FBdEJELHNCQXNCQyJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJyZXRyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUV6RixLQUFLLFVBQVUsS0FBSyxDQUFJLEVBQW1DO0lBQ2pFLElBQUksU0FBNEIsQ0FBQztJQUVqQyxLQUFLLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFO1FBQ25DLElBQUk7WUFDSCxPQUFPLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3JCO1FBQUMsT0FBTyxHQUFHLEVBQUU7WUFDYixJQUFJLENBQUMsbUVBQW1FLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsRUFBRTtnQkFDM0YsTUFBTSxHQUFHLENBQUM7YUFDVjtZQUVELFNBQVMsR0FBRyxHQUFHLENBQUM7WUFDaEIsTUFBTSxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUNqRSxPQUFPLENBQUMsR0FBRyxDQUFDLCtCQUErQixNQUFNLE9BQU8sQ0FBQyxDQUFDO1lBRTFELDBDQUEwQztZQUMxQyxNQUFNLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1NBQzlDO0tBQ0Q7SUFFRCxPQUFPLENBQUMsR0FBRyxDQUFDLDZCQUE2QixDQUFDLENBQUM7SUFDM0MsTUFBTSxTQUFTLENBQUM7QUFDakIsQ0FBQztBQXRCRCxzQkFzQkMifQ== \ No newline at end of file diff --git a/build/azure-pipelines/common/retry.ts b/build/azure-pipelines/common/retry.ts index ed232245dec..9b28b4fd956 100644 --- a/build/azure-pipelines/common/retry.ts +++ b/build/azure-pipelines/common/retry.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export async function retry(fn: () => Promise): Promise { +export async function retry(fn: (attempt: number) => Promise): Promise { let lastError: Error | undefined; for (let run = 1; run <= 10; run++) { try { - return await fn(); + return await fn(run); } catch (err) { if (!/ECONNRESET|CredentialUnavailableError|Audience validation failed/i.test(err.message)) { throw err; diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 0b445933265..1ca8c9ec1a9 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -13,6 +13,7 @@ steps: env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Download Electron and Playwright + retryCountOnTaskFailure: 3 - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -84,13 +85,13 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) displayName: Run integration tests (Electron) timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser webkit env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web displayName: Run integration tests (Browser, Webkit) timeoutInMinutes: 20 @@ -101,7 +102,7 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ ./scripts/test-remote-integration.sh env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) displayName: Run integration tests (Remote) timeoutInMinutes: 20 @@ -133,7 +134,7 @@ steps: - script: yarn smoketest-no-compile --web --tracing --headless env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) @@ -144,7 +145,7 @@ steps: APP_NAME="`ls $APP_ROOT | head -n 1`" yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) timeoutInMinutes: 20 displayName: Run smoke tests (Remote) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index f7810b6f076..3d8e6dc67b8 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -111,17 +111,34 @@ steps: - template: ../common/install-builtin-extensions.yml - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + echo "##vso[task.setvariable variable=BUILT_CLIENT]true" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client - - script: yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-darwin-$(VSCODE_ARCH) ../vscode-server-darwin-$(VSCODE_ARCH) # TODO@joaomoreno + ARCHIVE_PATH=".build/darwin/server/vscode-server-darwin-$(VSCODE_ARCH).zip" + mkdir -p $(dirname $ARCHIVE_PATH) + (cd .. && zip -Xry $(Build.SourcesDirectory)/$ARCHIVE_PATH vscode-server-darwin-$(VSCODE_ARCH)) + echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server - - script: yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-web-darwin-$(VSCODE_ARCH) ../vscode-server-darwin-$(VSCODE_ARCH)-web # TODO@joaomoreno + ARCHIVE_PATH=".build/darwin/server/vscode-server-darwin-$(VSCODE_ARCH)-web.zip" + mkdir -p $(dirname $ARCHIVE_PATH) + (cd .. && zip -Xry $(Build.SourcesDirectory)/$ARCHIVE_PATH vscode-server-darwin-$(VSCODE_ARCH)-web) + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server (web) @@ -176,18 +193,18 @@ steps: DEBUG=electron-osx-sign* node build/darwin/sign.js $(agent.builddirectory) displayName: Set Hardened Entitlements - - script: cd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * - displayName: Archive build - - script: | set -e + ARCHIVE_PATH=".build/darwin/client/VSCode-darwin-$(VSCODE_ARCH).zip" + mkdir -p $(dirname $ARCHIVE_PATH) + (cd ../VSCode-darwin-$(VSCODE_ARCH) && zip -Xry $(Build.SourcesDirectory)/$ARCHIVE_PATH *) + echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" + condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) + displayName: Package client - # package Remote Extension Host - pushd .. && mv vscode-reh-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH) && zip -Xry vscode-server-darwin-$(VSCODE_ARCH).zip vscode-server-darwin-$(VSCODE_ARCH) && popd - - # package Remote Extension Host (Web) - pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd - displayName: Prepare to publish servers + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -195,39 +212,31 @@ steps: BuildDropPath: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) PackageName: Visual Studio Code - - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) PackageName: Visual Studio Code Server + - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: $(ARTIFACT_PREFIX)sbom_client_darwin_$(VSCODE_ARCH)_sbom + - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_server_darwin_$(VSCODE_ARCH)_sbom - - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip - artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + - publish: $(CLIENT_PATH) + artifact: $(ARTIFACT_PREFIX)unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish client archive - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip - artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned + - publish: $(SERVER_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) displayName: Publish server archive - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip - artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned + - publish: $(WEB_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web server archive - - - task: AzureCLI@2 - inputs: - azureSubscription: "vscode-builds-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" diff --git a/build/azure-pipelines/distro/apply-cli-patches.js b/build/azure-pipelines/distro/apply-cli-patches.js deleted file mode 100644 index 68dec380e89..00000000000 --- a/build/azure-pipelines/distro/apply-cli-patches.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const cp = require("child_process"); -const toml = require("@iarna/toml"); -function log(...args) { - console.log(`[${new Date().toLocaleTimeString('en', { hour12: false })}]`, '[distro]', ...args); -} -log(`Applying CLI patches...`); -const basePath = `.build/distro/cli-patches`; -const patchTomlSuffix = '.patch.toml'; -function deepMerge(target, source) { - for (const [key, value] of Object.entries(source)) { - if (value && typeof value === 'object' && !Array.isArray(value)) { - if (!target.hasOwnProperty(key)) { - target[key] = value; - } - else { - deepMerge(target[key], value); - } - } - else { - target[key] = value; - } - } - return target; -} -for (const patch of fs.readdirSync(basePath)) { - if (patch.endsWith(patchTomlSuffix)) { - // this does not support nested filepaths, but that's fine for now... - const originalPath = `cli/${patch.slice(0, -patchTomlSuffix.length)}.toml`; - const contents = toml.parse(fs.readFileSync(originalPath, 'utf8')); - deepMerge(contents, toml.parse(fs.readFileSync(`${basePath}/${patch}`, 'utf8'))); - fs.writeFileSync(originalPath, toml.stringify(contents)); - } - else { - cp.execSync(`git apply --ignore-whitespace --ignore-space-change ${basePath}/${patch}`, { stdio: 'inherit' }); - } - log('Applied CLI patch:', patch, 'āœ”ļøŽ'); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwbHktY2xpLXBhdGNoZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhcHBseS1jbGktcGF0Y2hlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLHlCQUF5QjtBQUN6QixvQ0FBb0M7QUFDcEMsb0NBQW9DO0FBRXBDLFNBQVMsR0FBRyxDQUFDLEdBQUcsSUFBVztJQUMxQixPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO0FBQ2pHLENBQUM7QUFFRCxHQUFHLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUUvQixNQUFNLFFBQVEsR0FBRywyQkFBMkIsQ0FBQztBQUM3QyxNQUFNLGVBQWUsR0FBRyxhQUFhLENBQUM7QUFFdEMsU0FBUyxTQUFTLENBQUMsTUFBVyxFQUFFLE1BQVc7SUFDMUMsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUU7UUFDbEQsSUFBSSxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUNoRSxJQUFJLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsRUFBRTtnQkFDaEMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQzthQUNwQjtpQkFBTTtnQkFDTixTQUFTLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO2FBQzlCO1NBQ0Q7YUFBTTtZQUNOLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUM7U0FDcEI7S0FDRDtJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELEtBQUssTUFBTSxLQUFLLElBQUksRUFBRSxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRTtJQUM3QyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsZUFBZSxDQUFDLEVBQUU7UUFDcEMscUVBQXFFO1FBQ3JFLE1BQU0sWUFBWSxHQUFHLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQztRQUMzRSxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDbkUsU0FBUyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxRQUFRLElBQUksS0FBSyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pGLEVBQUUsQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztLQUN6RDtTQUFNO1FBQ04sRUFBRSxDQUFDLFFBQVEsQ0FBQyx1REFBdUQsUUFBUSxJQUFJLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7S0FDOUc7SUFDRCxHQUFHLENBQUMsb0JBQW9CLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO0NBQ3ZDIn0= \ No newline at end of file diff --git a/build/azure-pipelines/distro/apply-cli-patches.ts b/build/azure-pipelines/distro/apply-cli-patches.ts deleted file mode 100644 index 65c39fda363..00000000000 --- a/build/azure-pipelines/distro/apply-cli-patches.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as fs from 'fs'; -import * as cp from 'child_process'; -import * as toml from '@iarna/toml'; - -function log(...args: any[]): void { - console.log(`[${new Date().toLocaleTimeString('en', { hour12: false })}]`, '[distro]', ...args); -} - -log(`Applying CLI patches...`); - -const basePath = `.build/distro/cli-patches`; -const patchTomlSuffix = '.patch.toml'; - -function deepMerge(target: any, source: any): any { - for (const [key, value] of Object.entries(source)) { - if (value && typeof value === 'object' && !Array.isArray(value)) { - if (!target.hasOwnProperty(key)) { - target[key] = value; - } else { - deepMerge(target[key], value); - } - } else { - target[key] = value; - } - } - return target; -} - -for (const patch of fs.readdirSync(basePath)) { - if (patch.endsWith(patchTomlSuffix)) { - // this does not support nested filepaths, but that's fine for now... - const originalPath = `cli/${patch.slice(0, -patchTomlSuffix.length)}.toml`; - const contents = toml.parse(fs.readFileSync(originalPath, 'utf8')); - deepMerge(contents, toml.parse(fs.readFileSync(`${basePath}/${patch}`, 'utf8'))); - fs.writeFileSync(originalPath, toml.stringify(contents)); - } else { - cp.execSync(`git apply --ignore-whitespace --ignore-space-change ${basePath}/${patch}`, { stdio: 'inherit' }); - } - log('Applied CLI patch:', patch, 'āœ”ļøŽ'); -} diff --git a/build/azure-pipelines/linux/install.sh b/build/azure-pipelines/linux/install.sh new file mode 100755 index 00000000000..b8960fc5fd4 --- /dev/null +++ b/build/azure-pipelines/linux/install.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e + +# To workaround the issue of yarn not respecting the registry value from .npmrc +yarn config set registry "$NPM_REGISTRY" + +if [ -z "$CC" ] || [ -z "$CXX" ]; then + # Download clang based on chromium revision used by vscode + curl -s https://raw.githubusercontent.com/chromium/chromium/108.0.5359.215/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + + # Download libcxx headers and objects from upstream electron releases + DEBUG=libcxx-fetcher \ + VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \ + VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \ + VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \ + VSCODE_ARCH="$npm_config_arch" \ + node build/linux/libcxx-fetcher.js + + # Set compiler toolchain + # Flags for the client build are based on + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:build/config/c++/BUILD.gn + export CC=$PWD/.build/CR_Clang/bin/clang + export CXX=$PWD/.build/CR_Clang/bin/clang++ + export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr" + export LDFLAGS="-stdlib=libc++ -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -Wl,--lto-O0" + export VSCODE_REMOTE_CC=$(which gcc) + export VSCODE_REMOTE_CXX=$(which g++) +fi + +for i in {1..5}; do # try 5 times + yarn --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." +done diff --git a/build/azure-pipelines/linux/prepare-publish.sh b/build/azure-pipelines/linux/prepare-publish.sh deleted file mode 100755 index 891fa8024ef..00000000000 --- a/build/azure-pipelines/linux/prepare-publish.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -set -e -REPO="$(pwd)" -ROOT="$REPO/.." - -# Publish tarball -PLATFORM_LINUX="linux-$VSCODE_ARCH" -BUILDNAME="VSCode-$PLATFORM_LINUX" -BUILD_VERSION="$(date +%s)" -[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$VSCODE_ARCH-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$VSCODE_ARCH-$BUILD_VERSION.tar.gz" -TARBALL_PATH="$ROOT/$TARBALL_FILENAME" - -rm -rf $ROOT/code-*.tar.* -(cd $ROOT && tar -czf $TARBALL_PATH $BUILDNAME) - -# Publish Remote Extension Host -LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" -SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" -SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" -SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - -rm -rf $ROOT/vscode-server-*.tar.* -(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - -# Publish Remote Extension Host (Web) -LEGACY_SERVER_BUILD_NAME="vscode-reh-web-$PLATFORM_LINUX" -SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX-web" -SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX-web.tar.gz" -SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - -rm -rf $ROOT/vscode-server-*-web.tar.* -(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - -# Publish DEB -case $VSCODE_ARCH in - x64) DEB_ARCH="amd64" ;; - *) DEB_ARCH="$VSCODE_ARCH" ;; -esac - -PLATFORM_DEB="linux-deb-$VSCODE_ARCH" -DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" -DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" - -# Publish RPM -case $VSCODE_ARCH in - x64) RPM_ARCH="x86_64" ;; - armhf) RPM_ARCH="armv7hl" ;; - arm64) RPM_ARCH="aarch64" ;; - *) RPM_ARCH="$VSCODE_ARCH" ;; -esac - -PLATFORM_RPM="linux-rpm-$VSCODE_ARCH" -RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" -RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" - -# Publish Snap -# Pack snap tarball artifact, in order to preserve file perms -mkdir -p $REPO/.build/linux/snap-tarball -SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" -rm -rf $SNAP_TARBALL_PATH -(cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) - -# Export DEB_PATH, RPM_PATH -echo "##vso[task.setvariable variable=DEB_PATH]$DEB_PATH" -echo "##vso[task.setvariable variable=RPM_PATH]$RPM_PATH" -echo "##vso[task.setvariable variable=TARBALL_PATH]$TARBALL_PATH" diff --git a/build/azure-pipelines/linux/product-build-linux-test.yml b/build/azure-pipelines/linux/product-build-linux-test.yml index 75e73b679a8..bb75be37e4b 100644 --- a/build/azure-pipelines/linux/product-build-linux-test.yml +++ b/build/azure-pipelines/linux/product-build-linux-test.yml @@ -13,17 +13,7 @@ steps: env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Download Electron and Playwright - - - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - set -e - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start - displayName: Setup build environment + retryCountOnTaskFailure: 3 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - script: | @@ -122,13 +112,13 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) displayName: Run integration tests (Electron) timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser chromium env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)-web displayName: Run integration tests (Browser, Chromium) timeoutInMinutes: 20 @@ -140,7 +130,7 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ ./scripts/test-remote-integration.sh env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) displayName: Run integration tests (Remote) timeoutInMinutes: 20 @@ -180,7 +170,7 @@ steps: - script: yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)-web timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) @@ -188,7 +178,7 @@ steps: set -e yarn gulp compile-extension:vscode-test-resolver APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" timeoutInMinutes: 20 displayName: Run smoke tests (Remote) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 38522b2d00e..192cbb38905 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -41,15 +41,28 @@ steps: - script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz displayName: Extract compilation output - - script: | - set -e - # Start X server - /etc/init.d/xvfb start - # Start dbus session - DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) - echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" - displayName: Setup system services - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: | + set -e + # Start X server + sudo apt-get update + sudo apt-get install -y pkg-config \ + libxss1 \ + dbus \ + xvfb \ + libgtk-3-0 \ + libgbm1 \ + libxkbfile-dev \ + libsecret-1-dev \ + libkrb5-dev + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + # Start dbus session + sudo mkdir -p /var/run/dbus + DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) + echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" + displayName: Setup system services - script: node build/setup-npm-registry.js $NPM_REGISTRY condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) @@ -72,7 +85,10 @@ steps: - script: | set -e npm config set registry "$NPM_REGISTRY" --location=project - npm config set always-auth=true --location=project + # npm >v7 deprecated the `always-auth` config option, refs npm/cli@72a7eeb + # following is a workaround for yarn to send authorization header + # for GET requests to the registry. + echo "always-auth=true" >> .npmrc yarn config set registry "$NPM_REGISTRY" condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM & Yarn @@ -83,17 +99,6 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - # TODO@joaomoreno TODO@deepak1556 this should be part of the base image - - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: | - sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg - sudo mkdir -m 0755 -p /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg - echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null - sudo apt update && sudo apt install -y docker-ce-cli - displayName: Install Docker client - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - ${{ if and(ne(parameters.VSCODE_QUALITY, 'oss'), or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64'))) }}: - task: Docker@1 displayName: "Pull Docker image" @@ -101,73 +106,69 @@ steps: azureSubscriptionEndpoint: "vscode-builds-subscription" azureContainerRegistry: vscodehub.azurecr.io command: "Run an image" - imageName: vscode-linux-build-agent:centos7-devtoolset8-${{ parameters.VSCODE_ARCH }} + imageName: vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) containerCommand: uname condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - ${{ if and(ne(parameters.VSCODE_QUALITY, 'oss'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - displayName: Register Docker QEMU - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'arm64')) - - - script: | - set -e - - for i in {1..5}; do # try 5 times - yarn --cwd build --frozen-lockfile --check-files && break - if [ $i -eq 3 ]; then - echo "Yarn failed too many times" >&2 - exit 1 - fi - echo "Yarn failed $i, trying again..." - done - - if [ -z "$CC" ] || [ -z "$CXX" ]; then - # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/108.0.5359.215/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux - # Download libcxx headers and objects from upstream electron releases - DEBUG=libcxx-fetcher \ - VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \ - VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \ - VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \ - VSCODE_ARCH="$(NPM_ARCH)" \ - node build/linux/libcxx-fetcher.js - # Set compiler toolchain - # Flags for the client build are based on - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:build/config/c++/BUILD.gn - export CC=$PWD/.build/CR_Clang/bin/clang - export CXX=$PWD/.build/CR_Clang/bin/clang++ - export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr" - export LDFLAGS="-stdlib=libc++ -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -Wl,--lto-O0" - export VSCODE_REMOTE_CC=$(which gcc) - export VSCODE_REMOTE_CXX=$(which g++) - fi - - for i in {1..5}; do # try 5 times - yarn --frozen-lockfile --check-files && break - if [ $i -eq 3 ]; then - echo "Yarn failed too many times" >&2 - exit 1 - fi - echo "Yarn failed $i, trying again..." - done - env: - npm_config_arch: $(NPM_ARCH) - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - ${{ if and(ne(parameters.VSCODE_QUALITY, 'oss'), or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64'))) }}: - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-${{ parameters.VSCODE_ARCH }} - displayName: Install dependencies - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + + for i in {1..5}; do # try 5 times + yarn --cwd build --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + + docker run -e GITHUB_TOKEN -e npm_config_arch -e NPM_REGISTRY \ + -e VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME -e VSCODE_HOST_MOUNT \ + -e ELECTRON_SKIP_BINARY_DOWNLOAD -e PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD \ + -v /mnt/vss/_work/1/s:/home/builduser/vscode -v /mnt/vss/_work/1/s/.build/.netrc:/home/builduser/.netrc \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -u 1000:1000 \ + -w /home/builduser/vscode vscodehub.azurecr.io/vscode-linux-build-agent:bionic-$(VSCODE_ARCH) \ + /bin/bash -c "./build/azure-pipelines/linux/install.sh" + + sudo chown -R $USER:$USER /mnt/vss/_work/1/s + env: + npm_config_arch: $(NPM_ARCH) + NPM_REGISTRY: "$(NPM_REGISTRY)" + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + VSCODE_HOST_MOUNT: "/mnt/vss/_work/1/s" + ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: + VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) + displayName: Install dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: node build/azure-pipelines/distro/mixin-npm condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) displayName: Mixin distro node modules + - ${{ else }}: + - script: | + set -e + + for i in {1..5}; do # try 5 times + yarn --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + env: + npm_config_arch: $(NPM_ARCH) + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: | set -e node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt @@ -183,17 +184,59 @@ steps: - template: ../common/install-builtin-extensions.yml - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + ARCHIVE_PATH=".build/linux/client/code-${{ parameters.VSCODE_QUALITY }}-$(VSCODE_ARCH)-$(date +%s).tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client - - script: yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + - ${{ if ne(parameters.VSCODE_CIBUILD, true) }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: $(ARTIFACT_PREFIX)vscode_cli_linux_$(VSCODE_ARCH)_cli + patterns: "**" + path: $(Build.ArtifactStagingDirectory)/cli + displayName: Download VS Code CLI + + - script: | + set -e + tar -xzvf $(Build.ArtifactStagingDirectory)/cli/*.tar.gz -C $(Build.ArtifactStagingDirectory)/cli + CLI_APP_NAME=$(node -p "require(\"$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/resources/app/product.json\").tunnelApplicationName") + APP_NAME=$(node -p "require(\"$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/resources/app/product.json\").applicationName") + mv $(Build.ArtifactStagingDirectory)/cli/$APP_NAME $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/$CLI_APP_NAME + displayName: Mix in CLI + + - script: | + set -e + tar -czf $CLIENT_PATH -C .. VSCode-linux-$(VSCODE_ARCH) + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Archive client + + - script: | + set -e + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH) # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/server/vscode-server-linux-$(VSCODE_ARCH).tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH) + echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server - - script: yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-web-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH)-web # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/web/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH)-web + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server (web) @@ -213,28 +256,45 @@ steps: VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: - - task: DownloadPipelineArtifact@2 - inputs: - artifact: vscode_cli_linux_$(VSCODE_ARCH)_cli - patterns: "**" - path: $(Build.ArtifactStagingDirectory)/cli - displayName: Download VS Code CLI + - script: | + set -e + docker run -v /mnt/vss/_work/1/s:/home/builduser/vscode \ + -v /mnt/vss/_work/1/s/.build/.netrc:/home/builduser/.netrc \ + -v /mnt/vss/_work/1/VSCode-linux-$(VSCODE_ARCH):/home/builduser/VSCode-linux-$(VSCODE_ARCH) \ + -u 1000:1000 \ + -w /home/builduser/vscode vscodehub.azurecr.io/vscode-linux-build-agent:bionic-$(VSCODE_ARCH) \ + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-deb" + displayName: Prepare deb package + + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + echo "##vso[task.setvariable variable=DEB_PATH]$(ls .build/linux/deb/*/deb/*.deb)" + displayName: Build deb package + + - script: | + set -e + docker run -v /mnt/vss/_work/1/s:/home/builduser/vscode \ + -v /mnt/vss/_work/1/s/.build/.netrc:/home/builduser/.netrc \ + -v /mnt/vss/_work/1/VSCode-linux-$(VSCODE_ARCH):/home/builduser/VSCode-linux-$(VSCODE_ARCH) \ + -u 1000:1000 \ + -w /home/builduser/vscode vscodehub.azurecr.io/vscode-linux-build-agent:bionic-$(VSCODE_ARCH) \ + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-rpm" + displayName: Prepare rpm package + + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + echo "##vso[task.setvariable variable=RPM_PATH]$(ls .build/linux/rpm/*/*.rpm)" + displayName: Build rpm package - script: | set -e - tar -xzvf $(Build.ArtifactStagingDirectory)/cli/*.tar.gz -C $(Build.ArtifactStagingDirectory)/cli - CLI_APP_NAME=$(node -p "require(\"$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/resources/app/product.json\").tunnelApplicationName") - APP_NAME=$(node -p "require(\"$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/resources/app/product.json\").applicationName") - mv $(Build.ArtifactStagingDirectory)/cli/$APP_NAME $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/$CLI_APP_NAME - displayName: Make CLI executable - - - script: yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" - displayName: Build deb package - - - script: yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" - displayName: Build rpm package - - - script: yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + ARCHIVE_PATH=".build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar -czf $ARCHIVE_PATH -C .build/linux snap + echo "##vso[task.setvariable variable=SNAP_PATH]$ARCHIVE_PATH" displayName: Prepare snap package - task: UseDotNet@2 @@ -248,8 +308,9 @@ steps: - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' displayName: Codesign rpm - - script: ./build/azure-pipelines/linux/prepare-publish.sh - displayName: Prepare for Publish + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -257,42 +318,47 @@ steps: BuildDropPath: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) PackageName: Visual Studio Code - - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_linux_$(VSCODE_ARCH)_sbom - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: BuildDropPath: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) PackageName: Visual Studio Code Server + - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: $(ARTIFACT_PREFIX)sbom_vscode_client_linux_$(VSCODE_ARCH) + - publish: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_vscode_server_linux_$(VSCODE_ARCH) + + - publish: $(CLIENT_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) + displayName: Publish client archive + + - publish: $(SERVER_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) + displayName: Publish server archive + + - publish: $(WEB_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) + displayName: Publish web server archive - publish: $(DEB_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package + artifact: $(ARTIFACT_PREFIX)vscode_client_linux_$(VSCODE_ARCH)_deb-package + condition: and(succeededOrFailed(), ne(variables['DEB_PATH'], '')) displayName: Publish deb package - publish: $(RPM_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package + artifact: $(ARTIFACT_PREFIX)vscode_client_linux_$(VSCODE_ARCH)_rpm-package + condition: and(succeededOrFailed(), ne(variables['RPM_PATH'], '')) displayName: Publish rpm package - - publish: $(TARBALL_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish client archive + - publish: $(SNAP_PATH) + artifact: $(ARTIFACT_PREFIX)snap-$(VSCODE_ARCH) + condition: and(succeededOrFailed(), ne(variables['SNAP_PATH'], '')) + displayName: Publish snap pre-package - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH).tar.gz - artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish server archive - - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz - artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish web server archive - - - task: PublishPipelineArtifact@0 - displayName: "Publish Pipeline Artifact" - inputs: - artifactName: "snap-$(VSCODE_ARCH)" - targetPath: .build/linux/snap-tarball diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 9002fcff5d7..c028cc58e04 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -27,15 +27,13 @@ steps: sudo apt-get install -y yarn # Define variables - REPO="$(pwd)" - SNAP_ROOT="$REPO/.build/linux/snap/$(VSCODE_ARCH)" + SNAP_ROOT="$(pwd)/.build/linux/snap/$(VSCODE_ARCH)" # Install build dependencies (cd build && yarn) # Unpack snap tarball artifact, in order to preserve file perms - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" - (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) + (cd .build/linux && tar -xzf snap-tarball/snap-$(VSCODE_ARCH).tar.gz) # Create snap package BUILD_VERSION="$(date +%s)" diff --git a/build/azure-pipelines/oss/product-build-pr-cache-linux.yml b/build/azure-pipelines/oss/product-build-pr-cache-linux.yml index 97eba56abc3..97c5ddd78ff 100644 --- a/build/azure-pipelines/oss/product-build-pr-cache-linux.yml +++ b/build/azure-pipelines/oss/product-build-pr-cache-linux.yml @@ -39,6 +39,10 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication + - script: sudo apt-get update && sudo apt-get install -y libkrb5-dev + displayName: Install build dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: | set -e for i in {1..5}; do # try 5 times diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index 789996060ec..90f85c5817f 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -105,6 +105,7 @@ jobs: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -121,6 +122,7 @@ jobs: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -137,6 +139,7 @@ jobs: # - template: win32/product-build-win32.yml # parameters: # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_ARCH: x64 # VSCODE_RUN_UNIT_TESTS: false # VSCODE_RUN_INTEGRATION_TESTS: false # VSCODE_RUN_SMOKE_TESTS: true diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 7407cd1825b..2d4ad8aa84a 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -136,23 +136,13 @@ variables: value: true - name: Codeql.SkipTaskAutoInjection value: true + - name: ARTIFACT_PREFIX + value: '' name: "$(Date:yyyyMMdd).$(Rev:r) (${{ parameters.VSCODE_QUALITY }})" resources: containers: - - container: vscode-bionic-x64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-x64 - endpoint: VSCodeHub - options: --user 0:0 --cap-add SYS_ADMIN - - container: vscode-arm64 - image: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-arm64 - endpoint: VSCodeHub - options: --user 0:0 --cap-add SYS_ADMIN - - container: vscode-armhf - image: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-armhf - endpoint: VSCodeHub - options: --user 0:0 --cap-add SYS_ADMIN - container: snapcraft image: vscodehub.azurecr.io/vscode-linux-build-agent:snapcraft-x64 endpoint: VSCodeHub @@ -282,6 +272,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -295,6 +286,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -308,6 +300,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -322,6 +315,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -345,10 +339,11 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: ia32 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} - VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: true - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - job: WindowsARM64 @@ -359,6 +354,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: arm64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -374,7 +370,6 @@ stages: - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: - job: Linuxx64UnitTest displayName: Unit Tests - container: vscode-bionic-x64 variables: VSCODE_ARCH: x64 NPM_ARCH: x64 @@ -390,7 +385,6 @@ stages: VSCODE_RUN_SMOKE_TESTS: false - job: Linuxx64IntegrationTest displayName: Integration Tests - container: vscode-bionic-x64 variables: VSCODE_ARCH: x64 NPM_ARCH: x64 @@ -406,7 +400,6 @@ stages: VSCODE_RUN_SMOKE_TESTS: false - job: Linuxx64SmokeTest displayName: Smoke Tests - container: vscode-bionic-x64 variables: VSCODE_ARCH: x64 NPM_ARCH: x64 @@ -423,7 +416,6 @@ stages: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}: - job: Linuxx64 - container: vscode-bionic-x64 variables: VSCODE_ARCH: x64 NPM_ARCH: x64 @@ -450,7 +442,6 @@ stages: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}: - job: LinuxArmhf - container: vscode-armhf variables: VSCODE_ARCH: armhf NPM_ARCH: arm @@ -466,7 +457,6 @@ stages: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}: - job: LinuxArm64 - container: vscode-arm64 variables: VSCODE_ARCH: arm64 NPM_ARCH: arm64 @@ -491,6 +481,7 @@ stages: - job: LinuxAlpine variables: VSCODE_ARCH: x64 + NPM_ARCH: x64 steps: - template: alpine/product-build-alpine.yml @@ -499,6 +490,7 @@ stages: timeoutInMinutes: 120 variables: VSCODE_ARCH: arm64 + NPM_ARCH: arm64 steps: - template: alpine/product-build-alpine.yml diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 8471cfdec3d..d8385278474 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -49,7 +49,7 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libsecret-1-dev libnotify-bin + - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libsecret-1-dev libnotify-bin libkrb5-dev displayName: Install build tools condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) diff --git a/build/azure-pipelines/product-publish.ps1 b/build/azure-pipelines/product-publish.ps1 index a9170d54f5c..fa4bf1aa31e 100644 --- a/build/azure-pipelines/product-publish.ps1 +++ b/build/azure-pipelines/product-publish.ps1 @@ -60,6 +60,7 @@ do { $artifacts | ForEach-Object { $artifactName = $_.name + if($set.Add($artifactName)) { Write-Host "Processing artifact: '$artifactName. Downloading from: $($_.resource.downloadUrl)" @@ -98,8 +99,11 @@ do { } | Format-Table exec { node build/azure-pipelines/common/createAsset.js $product $os $arch $type $asset.Name $asset.FullName } - $artifactName >> $ARTIFACT_PROCESSED_FILE_PATH } + + # Mark the artifact as processed. Make sure to keep the previously + # processed artifacts in the file as well, not just from this run. + $artifactName >> $ARTIFACT_PROCESSED_FILE_PATH } # Get the timeline and see if it says the other stage completed diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 556f1f23efa..95d1b6afefd 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -53,6 +53,10 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication + - script: sudo apt-get update && sudo apt-get install -y libkrb5-dev + displayName: Install build dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: | set -e for i in {1..5}; do # try 5 times @@ -87,7 +91,13 @@ steps: - template: ../common/install-builtin-extensions.yml - - script: yarn gulp vscode-web-min-ci + - script: | + set -e + yarn gulp vscode-web-min-ci + ARCHIVE_PATH=".build/web/vscode-web.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-web + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build @@ -133,20 +143,11 @@ steps: node build/azure-pipelines/upload-nlsmetadata displayName: Upload NLS Metadata - - script: | - set -e - REPO="$(pwd)" - ROOT="$REPO/.." + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix - WEB_BUILD_NAME="vscode-web" - WEB_TARBALL_FILENAME="vscode-web.tar.gz" - WEB_TARBALL_PATH="$ROOT/$WEB_TARBALL_FILENAME" - - rm -rf $ROOT/vscode-web.tar.* - - cd $ROOT && tar --owner=0 --group=0 -czf $WEB_TARBALL_PATH $WEB_BUILD_NAME - displayName: Prepare for publish - - - publish: $(Agent.BuildDirectory)/vscode-web.tar.gz - artifact: vscode_web_linux_standalone_archive-unsigned + - publish: $(WEB_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_web_linux_standalone_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web archive diff --git a/build/azure-pipelines/win32/prepare-publish.ps1 b/build/azure-pipelines/win32/prepare-publish.ps1 deleted file mode 100644 index d2870fe7871..00000000000 --- a/build/azure-pipelines/win32/prepare-publish.ps1 +++ /dev/null @@ -1,40 +0,0 @@ -. build/azure-pipelines/win32/exec.ps1 -$ErrorActionPreference = "Stop" - -$Arch = "$env:VSCODE_ARCH" -$Repo = "$(pwd)" -$Root = "$Repo\.." -$SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe" -$UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe" -$Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip" -$LegacyServer = "$Root\vscode-reh-win32-$Arch" -$Server = "$Root\vscode-server-win32-$Arch" -$ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip" -$LegacyWeb = "$Root\vscode-reh-web-win32-$Arch" -$Web = "$Root\vscode-server-win32-$Arch-web" -$WebZip = "$Repo\.build\vscode-server-win32-$Arch-web.zip" -$Build = "$Root\VSCode-win32-$Arch" - -# Create server archive -if ("$Arch" -ne "arm64") { - exec { xcopy $LegacyServer $Server /H /E /I } - exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r } - exec { xcopy $LegacyWeb $Web /H /E /I } - exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $WebZip $Web -r } -} - -# get version -$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json -$Version = $PackageJson.version - -$ARCHIVE_NAME = "VSCode-win32-$Arch-$Version.zip" -$SYSTEM_SETUP_NAME = "VSCodeSetup-$Arch-$Version.exe" -$USER_SETUP_NAME = "VSCodeUserSetup-$Arch-$Version.exe" - -# Set variables for upload -Move-Item $Zip "$Repo\.build\win32-$Arch\archive\$ARCHIVE_NAME" -Write-Host "##vso[task.setvariable variable=ARCHIVE_NAME]$ARCHIVE_NAME" -Move-Item $SystemExe "$Repo\.build\win32-$Arch\system-setup\$SYSTEM_SETUP_NAME" -Write-Host "##vso[task.setvariable variable=SYSTEM_SETUP_NAME]$SYSTEM_SETUP_NAME" -Move-Item $UserExe "$Repo\.build\win32-$Arch\user-setup\$USER_SETUP_NAME" -Write-Host "##vso[task.setvariable variable=USER_SETUP_NAME]$USER_SETUP_NAME" diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index 6ad4f2dffbc..3a24e95657a 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -1,6 +1,8 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_ARCH + type: string - name: VSCODE_RUN_UNIT_TESTS type: boolean - name: VSCODE_RUN_INTEGRATION_TESTS @@ -13,6 +15,7 @@ steps: env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Download Electron and Playwright + retryCountOnTaskFailure: 3 - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -32,14 +35,17 @@ steps: - powershell: .\scripts\test.bat --build --tfs "Unit Tests" displayName: Run unit tests (Electron) timeoutInMinutes: 15 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn test-node --build displayName: Run unit tests (node.js) timeoutInMinutes: 15 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn test-browser-no-install --sequential --build --browser chromium --tfs "Browser Unit Tests" displayName: Run unit tests (Browser, Chromium) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - powershell: | @@ -89,16 +95,21 @@ steps: $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } + $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" + exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } displayName: Run integration tests (Electron) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)-web" + exec { .\scripts\test-web-integration.bat --browser firefox } displayName: Run integration tests (Browser, Firefox) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -106,9 +117,12 @@ steps: $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } + $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" + exec { .\scripts\test-remote-integration.bat } displayName: Run integration tests (Remote) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - powershell: .\build\azure-pipelines\win32\listprocesses.bat @@ -131,12 +145,14 @@ steps: - powershell: yarn smoketest-no-compile --tracing --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" displayName: Run smoke tests (Electron) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn smoketest-no-compile --web --tracing --headless env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)-web displayName: Run smoke tests (Browser, Chromium) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn gulp compile-extension:vscode-test-resolver displayName: Compile test resolver extension @@ -144,9 +160,10 @@ steps: - powershell: yarn smoketest-no-compile --tracing --remote --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH) displayName: Run smoke tests (Remote) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: .\build\azure-pipelines\win32\listprocesses.bat displayName: Diagnostics after smoke test run diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ab985554bb7..6060ef2e6cb 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,6 +1,8 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_ARCH + type: string - name: VSCODE_CIBUILD type: boolean - name: VSCODE_RUN_UNIT_TESTS @@ -92,9 +94,12 @@ steps: $ErrorActionPreference = "Stop" # TODO: Should be replaced with upstream URL once https://github.com/nodejs/node-gyp/pull/2825 # gets merged. - exec { git clone https://github.com/rzhao271/node-gyp.git . } "Cloning rzhao271/node-gyp failed" - exec { git checkout 102b347da0c92c29f9c67df22e864e70249cf086 } "Checking out 102b347 failed" - exec { npm install } "Building rzhao271/node-gyp failed" + exec { git config --global user.email "vscode@microsoft.com" } "git config user.email failed" + exec { git config --global user.name "VSCode" } "git config user.name failed" + exec { git clone https://github.com/nodejs/node-gyp.git . } "Cloning nodejs/node-gyp failed" + exec { git checkout v9.4.0 } "Checking out v9.4.0 failed" + exec { git am --3way --whitespace=fix ../../build/npm/gyp/patches/gyp_spectre_mitigation_support.patch } "Apply spectre patch failed" + exec { npm install } "Building node-gyp failed" displayName: Install custom node-gyp workingDirectory: .build/node-gyp condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) @@ -134,7 +139,7 @@ steps: - template: ../common/install-builtin-extensions.yml - - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: - powershell: node build\lib\policies displayName: Generate Group Policy definitions retryCountOnTaskFailure: 3 @@ -146,7 +151,7 @@ steps: displayName: Transpile - ${{ else }}: - - ${{ if eq(parameters.VSCODE_QUALITY, 'insider') }}: + - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), eq(parameters.VSCODE_QUALITY, 'insider')) }}: - powershell: node build/win32/explorer-appx-fetcher .build/win32/appx displayName: Download Explorer Sparse Package @@ -154,31 +159,41 @@ steps: . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } + echo "##vso[task.setvariable variable=BUILT_CLIENT]true" echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build - - - powershell: yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Prepare Setup Package + displayName: Build client - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { yarn gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } - exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)" + mv ..\vscode-reh-win32-$(VSCODE_ARCH) ..\vscode-server-win32-$(VSCODE_ARCH) # TODO@joaomoreno + echo "##vso[task.setvariable variable=BUILT_SERVER]true" + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build Servers + displayName: Build server + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } + mv ..\vscode-reh-web-win32-$(VSCODE_ARCH) ..\vscode-server-win32-$(VSCODE_ARCH)-web # TODO@joaomoreno + echo "##vso[task.setvariable variable=BUILT_WEB]true" + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Build server (web) condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: - template: product-build-win32-test.yml parameters: VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_ARCH: ${{ parameters.VSCODE_ARCH }} VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} @@ -229,8 +244,42 @@ steps: displayName: Codesign context menu appx package - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - powershell: yarn gulp "vscode-win32-$(VSCODE_ARCH)-archive" - displayName: Package archive + - powershell: | + $PackageJson = Get-Content -Raw -Path ..\VSCode-win32-$(VSCODE_ARCH)\resources\app\package.json | ConvertFrom-Json + $Version = $PackageJson.version + echo "##vso[task.setvariable variable=VSCODE_VERSION]$Version" + condition: succeededOrFailed() + displayName: Get product version + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArchivePath = ".build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH)-$(VSCODE_VERSION).zip" + New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force + exec { 7z.exe a -tzip $ArchivePath -x!CodeSignSummary*.md ..\VSCode-win32-$(VSCODE_ARCH)\* -r } + echo "##vso[task.setvariable variable=CLIENT_PATH]$ArchivePath" + condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) + displayName: Package client + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH).zip" + New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH) -r } + echo "##vso[task.setvariable variable=SERVER_PATH]$ArchivePath" + condition: and(succeededOrFailed(), eq(variables['BUILT_SERVER'], 'true')) + displayName: Package server + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH)-web.zip" + New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)-web -r } + echo "##vso[task.setvariable variable=WEB_PATH]$ArchivePath" + condition: and(succeededOrFailed(), eq(variables['BUILT_WEB'], 'true')) + displayName: Package server (web) - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -239,11 +288,26 @@ steps: $env:ESRPAADUsername = "$(esrp-aad-username)" $env:ESRPAADPassword = "$(esrp-aad-password)" exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" --sign } - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } - displayName: Package setups + $SetupPath = ".build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + mv .build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe $SetupPath + echo "##vso[task.setvariable variable=SYSTEM_SETUP_PATH]$SetupPath" + displayName: Build system setup - - powershell: .\build\azure-pipelines\win32\prepare-publish.ps1 - displayName: Publish + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:ESRPPKI = "$(ESRP-PKI)" + $env:ESRPAADUsername = "$(esrp-aad-username)" + $env:ESRPAADPassword = "$(esrp-aad-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } + $SetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeUserSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $SetupPath + echo "##vso[task.setvariable variable=USER_SETUP_PATH]$SetupPath" + displayName: Build user setup + + - powershell: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -251,10 +315,6 @@ steps: BuildDropPath: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH) PackageName: Visual Studio Code - - publish: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_win32_$(VSCODE_ARCH)_sbom - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: @@ -262,29 +322,36 @@ steps: PackageName: Visual Studio Code Server condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) + - publish: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: $(ARTIFACT_PREFIX)sbom_client_win32_$(VSCODE_ARCH) + - publish: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: vscode_server_win32_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_server_win32_$(VSCODE_ARCH) condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\archive\$(ARCHIVE_NAME) - artifact: vscode_client_win32_$(VSCODE_ARCH)_archive + - publish: $(CLIENT_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_client_win32_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish archive - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\system-setup\$(SYSTEM_SETUP_NAME) - artifact: vscode_client_win32_$(VSCODE_ARCH)_setup + - publish: $(SERVER_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_server_win32_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), ne(variables['VSCODE_ARCH'], 'arm64')) + displayName: Publish server archive + + - publish: $(WEB_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_web_win32_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), ne(variables['VSCODE_ARCH'], 'arm64')) + displayName: Publish web server archive + + - publish: $(SYSTEM_SETUP_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_client_win32_$(VSCODE_ARCH)_setup + condition: and(succeededOrFailed(), ne(variables['SYSTEM_SETUP_PATH'], '')) displayName: Publish system setup - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\user-setup\$(USER_SETUP_NAME) - artifact: vscode_client_win32_$(VSCODE_ARCH)_user-setup + - publish: $(USER_SETUP_PATH) + artifact: $(ARTIFACT_PREFIX)vscode_client_win32_$(VSCODE_ARCH)_user-setup + condition: and(succeededOrFailed(), ne(variables['USER_SETUP_PATH'], '')) displayName: Publish user setup - - - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH).zip - artifact: vscode_server_win32_$(VSCODE_ARCH)_archive - displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH)-web.zip - artifact: vscode_web_win32_$(VSCODE_ARCH)_archive - displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index beb2b9730b0..68c55c6a134 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,27 +1,27 @@ -3ba067c6f338f9a525c4b697e9cf8e3c3b3d9f6abfdfb11fba47e053da0f3496 *electron-v22.3.14-darwin-arm64-symbols.zip -c08bf19e11c006346b210585cf0803cd0b07107a362a2414cc185f6a228afbf2 *electron-v22.3.14-darwin-arm64.zip -72ced94e7230d3138dd84acbf38dc593d4a93ec796a3a478f99aa6974030d79c *electron-v22.3.14-darwin-x64-symbols.zip -77c1c96411326b00d3ef7c9f6af96a3b4c2fa2314196fce3374fcf734dd8dc67 *electron-v22.3.14-darwin-x64.zip -d847c59f3835749dcdd5376daefb3a5992f1ed5d7693f871328296e1388fb69d *electron-v22.3.14-linux-arm64-symbols.zip -95bb9ee160c60b50ff25b307fb8bc36bdb5297d43c6e366f0b835f36c4f327c9 *electron-v22.3.14-linux-arm64.zip -38a51d81f9ffe6e2ebf25844999fddbeb4edc63ae22136af1502db373bb024ab *electron-v22.3.14-linux-armv7l-symbols.zip -bf589c74f07fe11586ffcf8c122d34b91c5ced08d54532ee883d1e025b6d1b02 *electron-v22.3.14-linux-armv7l.zip -28d0eda61ea736375c549d0955f36b7d3e3c2019453ef83d793dae8b0d74f461 *electron-v22.3.14-linux-x64-symbols.zip -89b72e40fb8b9106deda3e6ffa30dd80beaa8f2e2a9d037b55c034a5a27a7b60 *electron-v22.3.14-linux-x64.zip -b9ba15fcf7c60cf57e95fae731bc0c336e131ed4fac91b4c59d50a28407ca0b0 *electron-v22.3.14-win32-arm64-pdb.zip -9f375d01feeb9e28f9c0913a4e22be900c0a7ff4e51449bb9b859ce1bd18f9f3 *electron-v22.3.14-win32-arm64-symbols.zip -17e354aca0683f79d79f7fa7ecfa8a4381b356d04fa45ec0aa85b5f048151c10 *electron-v22.3.14-win32-arm64.zip -900ca316ce939547ab62847c8833a78c1002a69b936be7e9af328a3518a7d379 *electron-v22.3.14-win32-ia32-pdb.zip -90af7a48b4e722a3436b6a8893540fb746d99b4832ca48c355a63fa0930f6446 *electron-v22.3.14-win32-ia32-symbols.zip -487d811c7cf3282f4c3a17b5ab7ab1fd71dbc585449d77da3a9bf052657ac4ad *electron-v22.3.14-win32-ia32.zip -41ce6c3d87c89f6b48aac74649657a120c28c78513908996dc20e57a640d4653 *electron-v22.3.14-win32-x64-pdb.zip -57b35bfa186b64a9dd1eb2bb85141bb998d0378bb20ac8038718b41d16deb978 *electron-v22.3.14-win32-x64-symbols.zip -f45eba3faa7e10fb1c6e5cf044dd42733a7c8cb455de57647b74e7510b0b94b6 *electron-v22.3.14-win32-x64.zip -16a75de6e3e4643589237e6e1c680c43b4e77fe04918bfbe4408775b7e616afc *ffmpeg-v22.3.14-darwin-arm64.zip -92db0c163c326d33a516ebfc56c7bd4faae9456f4238dde916c580b459b8dc8d *ffmpeg-v22.3.14-darwin-x64.zip -59d2e2b2f2cc515a86a4e0cfd1116d10a8b25a8d58d45bb04de3512e156c944b *ffmpeg-v22.3.14-linux-arm64.zip -b9d3b227bee17666d395ee7882ef477a733c3eeef3f1d9f2e3616d2d02eb3376 *ffmpeg-v22.3.14-linux-armv7l.zip -fa07ef910b23a4ef4b6761bc16d20c0e70ff0259325c4d523129e2d9c5084174 *ffmpeg-v22.3.14-linux-x64.zip -7f744b657ae7c26f80cae0f2771a00edd368350229b85118a246573987dd6ff1 *ffmpeg-v22.3.14-win32-arm64.zip -562e04d2cf1c970b6128d66d08dfe8d88a28e54adf599293eee2bd6c292fd16b *ffmpeg-v22.3.14-win32-ia32.zip -f69510384ef912fd9b4961f97357789a4a36e8df6ff382aeeab23fbb063def9a *ffmpeg-v22.3.14-win32-x64.zip +fe6d7067770403c61c413d3176e9ddc1ee28e0650597c469dd4c2965feafa7a0 *electron-v22.3.18-darwin-arm64-symbols.zip +a11c41f2b1e740e77fccc1e2e299e89f370cd8153420976c1b16628733969af4 *electron-v22.3.18-darwin-arm64.zip +a5329fde23814813aaed4283f1cb27db1e321fcecb34a15a716325194a9f2aae *electron-v22.3.18-darwin-x64-symbols.zip +d3ecd733a174b8fd16927285f9e9f3a5d401c29578619a6c12aec5c3845d0d51 *electron-v22.3.18-darwin-x64.zip +c1486b7536d5e4ad16caf37e1fe20ea3f445a09a44fd8c2ee3369b537527479f *electron-v22.3.18-linux-arm64-symbols.zip +4857d182cffb853b0c85c96e4e99d20316f95068398b7ac5424641e1f2263465 *electron-v22.3.18-linux-arm64.zip +edeb1d723594efdf392180e9720e804c9decd52bfa64a177bd5602c11de0c250 *electron-v22.3.18-linux-armv7l-symbols.zip +109cd957e64c728bd1b921385250d413c9546c7ba44d191a9e6a62ea39eb093b *electron-v22.3.18-linux-armv7l.zip +7587452b5390122d262e4fb26bb01e5583633d18a6b714149c13e53df90be9fd *electron-v22.3.18-linux-x64-symbols.zip +8b65f6c6b960dd6bc52acbb0fc54f232dfa8a9d6ed0e1504ee6baf346c90598b *electron-v22.3.18-linux-x64.zip +a97c04ee4d952fe21cd7141fd0baf36a8d16c5ffd165ecc70640d3d6817f7b69 *electron-v22.3.18-win32-arm64-pdb.zip +b849fe8ecab51e846c51b4059c467f8b4aeccf1451420494dfbc90f427a2fd64 *electron-v22.3.18-win32-arm64-symbols.zip +e67daaf16c3e03ce7f64feceadaa3909677e6385f79eb806aab726611e28060f *electron-v22.3.18-win32-arm64.zip +96bb514d47db2976572311296f361b5e3e4a2182d6cdcaa696432625d7a1b5a8 *electron-v22.3.18-win32-ia32-pdb.zip +a0dbcad02fd4304b3696080c77f8f6835b144dcb63db5275f33289d487356bb1 *electron-v22.3.18-win32-ia32-symbols.zip +b21f0e260f3251bf6f4ee41c81f882b3f609d067eca578e56e4c3a7a47360ef2 *electron-v22.3.18-win32-ia32.zip +abef6b14a6e613ed4087012ce197dff132bd30c5f29e394d4d30285f2d3dd481 *electron-v22.3.18-win32-x64-pdb.zip +60e4b6a17811c9afa5a499a9c7c651b55e4419320b4706479d62225dc5d67f5d *electron-v22.3.18-win32-x64-symbols.zip +f81cd80551268dfd915f17f0be8d23da168a7efc0c10ca004d2e4edd48a017ae *electron-v22.3.18-win32-x64.zip +953b3bcf735e3d9270b605e46af54352095304c4bc9ab8448d242a9e62242acc *ffmpeg-v22.3.18-darwin-arm64.zip +b81bc30765d0e3cbea5f813e4342c24d6984dc60b505e138a5e477c322d120be *ffmpeg-v22.3.18-darwin-x64.zip +59d2e2b2f2cc515a86a4e0cfd1116d10a8b25a8d58d45bb04de3512e156c944b *ffmpeg-v22.3.18-linux-arm64.zip +b9d3b227bee17666d395ee7882ef477a733c3eeef3f1d9f2e3616d2d02eb3376 *ffmpeg-v22.3.18-linux-armv7l.zip +fa07ef910b23a4ef4b6761bc16d20c0e70ff0259325c4d523129e2d9c5084174 *ffmpeg-v22.3.18-linux-x64.zip +2ee952c58f5b74dfbefa3390a91d2fca663b306bf7dc497fefe91663b9f5d0f3 *ffmpeg-v22.3.18-win32-arm64.zip +44c90b3c0655a3db3c6bde43eb8619c7ce45a93439ed729c373425467d00e346 *ffmpeg-v22.3.18-win32-ia32.zip +3b77a7f06274b91eff73f001063efe6c2298197728b2524e381be3050c123c46 *ffmpeg-v22.3.18-win32-x64.zip diff --git a/build/filters.js b/build/filters.js index 095cf733f8c..5a8cf36ef84 100644 --- a/build/filters.js +++ b/build/filters.js @@ -32,6 +32,7 @@ module.exports.unicodeFilter = [ '**', '!**/ThirdPartyNotices.txt', + '!**/ThirdPartyNotices.cli.txt', '!**/LICENSE.{txt,rtf}', '!LICENSES.chromium.html', '!**/LICENSE', @@ -65,6 +66,7 @@ module.exports.indentationFilter = [ // except specific files '!**/ThirdPartyNotices.txt', + '!**/ThirdPartyNotices.cli.txt', '!**/LICENSE.{txt,rtf}', '!LICENSES.chromium.html', '!**/LICENSE', @@ -78,6 +80,7 @@ module.exports.indentationFilter = [ '!test/unit/assert.js', '!resources/linux/snap/electron-launch', '!build/ext.js', + '!build/npm/gyp/patches/gyp_spectre_mitigation_support.patch', // except specific folders '!test/automation/out/**', diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 90f75ccfabd..0d7d3c5b7f8 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -297,12 +297,14 @@ const BUILD_TARGETS = [ BUILD_TARGETS.forEach(({ arch }) => { const debArch = getDebPackageArch(arch); const prepareDebTask = task.define(`vscode-linux-${arch}-prepare-deb`, task.series(util.rimraf(`.build/linux/deb/${debArch}`), prepareDebPackage(arch))); - const buildDebTask = task.define(`vscode-linux-${arch}-build-deb`, task.series(prepareDebTask, buildDebPackage(arch))); + gulp.task(prepareDebTask); + const buildDebTask = task.define(`vscode-linux-${arch}-build-deb`, buildDebPackage(arch)); gulp.task(buildDebTask); const rpmArch = getRpmPackageArch(arch); const prepareRpmTask = task.define(`vscode-linux-${arch}-prepare-rpm`, task.series(util.rimraf(`.build/linux/rpm/${rpmArch}`), prepareRpmPackage(arch))); - const buildRpmTask = task.define(`vscode-linux-${arch}-build-rpm`, task.series(prepareRpmTask, buildRpmPackage(arch))); + gulp.task(prepareRpmTask); + const buildRpmTask = task.define(`vscode-linux-${arch}-build-rpm`, buildRpmPackage(arch)); gulp.task(buildRpmTask); const prepareSnapTask = task.define(`vscode-linux-${arch}-prepare-snap`, task.series(util.rimraf(`.build/linux/snap/${arch}`), prepareSnapPackage(arch))); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 6e9a6f331ba..674eb41a503 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -10,7 +10,6 @@ const path = require('path'); const fs = require('fs'); const assert = require('assert'); const cp = require('child_process'); -const _7z = require('7zip')['7z']; const util = require('./lib/util'); const task = require('./lib/task'); const pkg = require('../package.json'); @@ -21,8 +20,6 @@ const mkdirp = require('mkdirp'); const repoPath = path.dirname(__dirname); const buildPath = (/** @type {string} */ arch) => path.join(path.dirname(repoPath), `VSCode-win32-${arch}`); -const zipDir = (/** @type {string} */ arch) => path.join(repoPath, '.build', `win32-${arch}`, 'archive'); -const zipPath = (/** @type {string} */ arch) => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`); const setupDir = (/** @type {string} */ arch, /** @type {string} */ target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`); const issPath = path.join(__dirname, 'win32', 'code.iss'); const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup'))), 'bin', 'ISCC.exe'); @@ -101,6 +98,7 @@ function buildWin32Setup(arch, target) { AppMutex: product.win32MutexName, TunnelMutex: product.win32TunnelMutex, TunnelServiceMutex: product.win32TunnelServiceMutex, + TunnelApplicationName: product.tunnelApplicationName, ApplicationName: product.applicationName, Arch: arch, AppId: { 'ia32': ia32AppId, 'x64': x64AppId, 'arm64': arm64AppId }[arch], @@ -142,23 +140,6 @@ defineWin32SetupTasks('ia32', 'user'); defineWin32SetupTasks('x64', 'user'); defineWin32SetupTasks('arm64', 'user'); -/** - * @param {string} arch - */ -function archiveWin32Setup(arch) { - return cb => { - const args = ['a', '-tzip', zipPath(arch), '-x!CodeSignSummary*.md', '.', '-r']; - - cp.spawn(_7z, args, { stdio: 'inherit', cwd: buildPath(arch) }) - .on('error', cb) - .on('exit', () => cb(null)); - }; -} - -gulp.task(task.define('vscode-win32-ia32-archive', task.series(util.rimraf(zipDir('ia32')), archiveWin32Setup('ia32')))); -gulp.task(task.define('vscode-win32-x64-archive', task.series(util.rimraf(zipDir('x64')), archiveWin32Setup('x64')))); -gulp.task(task.define('vscode-win32-arm64-archive', task.series(util.rimraf(zipDir('arm64')), archiveWin32Setup('arm64')))); - /** * @param {string} arch */ diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 50d9b271d95..f83225cc974 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -86,10 +86,6 @@ "name": "vs/workbench/contrib/externalTerminal", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/feedback", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/files", "project": "vscode-workbench" diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 1875db4eceb..8929f43ee4a 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -211,8 +211,18 @@ "--vscode-editorHoverWidget-foreground", "--vscode-editorHoverWidget-highlightForeground", "--vscode-editorHoverWidget-statusBarBackground", - "--vscode-editorIndentGuide-activeBackground", - "--vscode-editorIndentGuide-background", + "--vscode-editorIndentGuide-activeBackground1", + "--vscode-editorIndentGuide-activeBackground2", + "--vscode-editorIndentGuide-activeBackground3", + "--vscode-editorIndentGuide-activeBackground4", + "--vscode-editorIndentGuide-activeBackground5", + "--vscode-editorIndentGuide-activeBackground6", + "--vscode-editorIndentGuide-background1", + "--vscode-editorIndentGuide-background2", + "--vscode-editorIndentGuide-background3", + "--vscode-editorIndentGuide-background4", + "--vscode-editorIndentGuide-background5", + "--vscode-editorIndentGuide-background6", "--vscode-editorInfo-background", "--vscode-editorInfo-border", "--vscode-editorInfo-foreground", @@ -306,7 +316,6 @@ "--vscode-inlineChatInput-border", "--vscode-inlineChatInput-focusBorder", "--vscode-inlineChatInput-placeholderForeground", - "--vscode-inlineChatrDiff-removed", "--vscode-input-background", "--vscode-input-border", "--vscode-input-foreground", @@ -688,6 +697,7 @@ "--background-light", "--dropdown-padding-bottom", "--dropdown-padding-top", + "--hover-maxWidth", "--insert-border-color", "--last-tab-margin-right", "--monaco-monospace-font", @@ -746,6 +756,7 @@ "--z-index-notebook-progress-bar", "--z-index-notebook-scrollbar", "--z-index-run-button-container", + "--z-index-notebook-sticky-scroll", "--zoom-factor" ] } diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js index 701db315b94..0c704b66341 100644 --- a/build/lib/tsb/transpiler.js +++ b/build/lib/tsb/transpiler.js @@ -293,7 +293,7 @@ class SwcTranspiler { tsx: false, decorators: true }, - target: 'es2020', + target: 'es2022', loose: false, minify: { compress: false, diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts index 312028fd1fa..a546ea63316 100644 --- a/build/lib/tsb/transpiler.ts +++ b/build/lib/tsb/transpiler.ts @@ -376,7 +376,7 @@ export class SwcTranspiler implements ITranspiler { tsx: false, decorators: true }, - target: 'es2020', + target: 'es2022', loose: false, minify: { compress: false, diff --git a/build/linux/debian/calculate-deps.js b/build/linux/debian/calculate-deps.js index d9839581a8f..80146ba40e2 100644 --- a/build/linux/debian/calculate-deps.js +++ b/build/linux/debian/calculate-deps.js @@ -71,10 +71,18 @@ function calculatePackageDeps(binaryPath, arch, sysroot) { // on the newer package, this hack skips the dep. This is safe because // libgcc-s1 is a dependency of libc6. This hack can be removed once // support for Debian Buster and Ubuntu Bionic are dropped. + // + // Remove kerberos native module related dependencies as the versions + // computed from sysroot will not satisfy the minimum supported distros + // Refs https://github.com/microsoft/vscode/issues/188881. + // TODO(deepak1556): remove this workaround in favor of computing the + // versions from build container for native modules. const filteredDeps = depsStr.split(', ').filter(dependency => { - return !dependency.startsWith('libgcc-s1'); + return !dependency.startsWith('libgcc-s1') && + !dependency.startsWith('libgssapi-krb5-2') && + !dependency.startsWith('libkrb5-3'); }).sort(); const requires = new Set(filteredDeps); return requires; } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsY3VsYXRlLWRlcHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjYWxjdWxhdGUtZGVwcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxpREFBMEM7QUFDMUMsMkJBQXlDO0FBQ3pDLDJCQUE0QjtBQUM1Qiw2QkFBOEI7QUFDOUIsc0RBQXNEO0FBQ3RELDJDQUE2QztBQUc3QyxTQUFnQixtQkFBbUIsQ0FBQyxLQUFlLEVBQUUsSUFBc0IsRUFBRSxPQUFlO0lBQzNGLE1BQU0sWUFBWSxHQUFrQixLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ2pHLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLENBQUMsMEJBQWMsQ0FBQyxDQUFDO0lBQ2xELFlBQVksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNyQyxPQUFPLFlBQVksQ0FBQztBQUNyQixDQUFDO0FBTEQsa0RBS0M7QUFFRCw2SEFBNkg7QUFDN0gsU0FBUyxvQkFBb0IsQ0FBQyxVQUFrQixFQUFFLElBQXNCLEVBQUUsT0FBZTtJQUN4RixJQUFJO1FBQ0gsSUFBSSxDQUFDLENBQUMsSUFBQSxhQUFRLEVBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxHQUFHLGNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRTtZQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsVUFBVSx1Q0FBdUMsQ0FBQyxDQUFDO1NBQzdFO0tBQ0Q7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNYLDhEQUE4RDtRQUM5RCxPQUFPLENBQUMsS0FBSyxDQUFDLGdCQUFnQixHQUFHLFVBQVUsR0FBRyxjQUFjLENBQUMsQ0FBQztLQUM5RDtJQUVELHdDQUF3QztJQUN4QyxNQUFNLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFO1FBQ3RFLE9BQU8sWUFBWSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEtBQUssS0FBSyxJQUFJLFlBQVksQ0FBQyxTQUFTLENBQUMsR0FBSSxDQUFDLElBQUksS0FBSyxVQUFVLENBQUM7SUFDakcsQ0FBQyxDQUFDLENBQUM7SUFDSCxNQUFNLGdCQUFnQixHQUFHLHVEQUF1RCxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLCtDQUErQyxDQUFDO0lBQzNKLE1BQU0sMkJBQTJCLEdBQUcsR0FBRyxJQUFBLFdBQU0sR0FBRSxvQkFBb0IsQ0FBQztJQUNwRSxNQUFNLE1BQU0sR0FBRyxJQUFBLHlCQUFTLEVBQUMsTUFBTSxFQUFFLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxFQUFFLDJCQUEyQixDQUFDLENBQUMsQ0FBQztJQUN4RixJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3hCLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0tBQzdFO0lBQ0QsTUFBTSxHQUFHLEdBQUcsQ0FBQywyQkFBMkIsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO0lBQ3JFLFFBQVEsSUFBSSxFQUFFO1FBQ2IsS0FBSyxPQUFPO1lBQ1gsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLE9BQU8sMkJBQTJCLEVBQy9DLEtBQUssT0FBTyx1QkFBdUIsQ0FBQyxDQUFDO1lBQ3RDLE1BQU07UUFDUCxLQUFLLE9BQU87WUFDWCxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssT0FBTyw4QkFBOEIsRUFDbEQsS0FBSyxPQUFPLDBCQUEwQixDQUFDLENBQUM7WUFDekMsTUFBTTtRQUNQLEtBQUssT0FBTztZQUNYLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxPQUFPLDRCQUE0QixFQUNoRCxLQUFLLE9BQU8sd0JBQXdCLENBQUMsQ0FBQztZQUN2QyxNQUFNO0tBQ1A7SUFDRCxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssT0FBTyxVQUFVLENBQUMsQ0FBQztJQUNqQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO0lBRS9DLE1BQU0sbUJBQW1CLEdBQUcsSUFBQSx5QkFBUyxFQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNyRSxJQUFJLG1CQUFtQixDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7UUFDckMsTUFBTSxJQUFJLEtBQUssQ0FBQyx3Q0FBd0MsbUJBQW1CLENBQUMsTUFBTSxjQUFjLG1CQUFtQixDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7S0FDL0g7SUFFRCxNQUFNLG1CQUFtQixHQUFHLGlCQUFpQixDQUFDO0lBQzlDLE1BQU0sWUFBWSxHQUFHLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3hGLElBQUksT0FBTyxHQUFHLEVBQUUsQ0FBQztJQUNqQixLQUFLLE1BQU0sSUFBSSxJQUFJLFlBQVksRUFBRTtRQUNoQyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsRUFBRTtZQUN6QyxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUNyRDtLQUNEO0lBQ0QseUVBQXlFO0lBQ3pFLDBFQUEwRTtJQUMxRSx5RUFBeUU7SUFDekUsdUVBQXVFO0lBQ3ZFLHFFQUFxRTtJQUNyRSwyREFBMkQ7SUFDM0QsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLEVBQUU7UUFDNUQsT0FBTyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDNUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDVixNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN2QyxPQUFPLFFBQVEsQ0FBQztBQUNqQixDQUFDIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsY3VsYXRlLWRlcHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjYWxjdWxhdGUtZGVwcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxpREFBMEM7QUFDMUMsMkJBQXlDO0FBQ3pDLDJCQUE0QjtBQUM1Qiw2QkFBOEI7QUFDOUIsc0RBQXNEO0FBQ3RELDJDQUE2QztBQUc3QyxTQUFnQixtQkFBbUIsQ0FBQyxLQUFlLEVBQUUsSUFBc0IsRUFBRSxPQUFlO0lBQzNGLE1BQU0sWUFBWSxHQUFrQixLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ2pHLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLENBQUMsMEJBQWMsQ0FBQyxDQUFDO0lBQ2xELFlBQVksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNyQyxPQUFPLFlBQVksQ0FBQztBQUNyQixDQUFDO0FBTEQsa0RBS0M7QUFFRCw2SEFBNkg7QUFDN0gsU0FBUyxvQkFBb0IsQ0FBQyxVQUFrQixFQUFFLElBQXNCLEVBQUUsT0FBZTtJQUN4RixJQUFJO1FBQ0gsSUFBSSxDQUFDLENBQUMsSUFBQSxhQUFRLEVBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxHQUFHLGNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRTtZQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsVUFBVSx1Q0FBdUMsQ0FBQyxDQUFDO1NBQzdFO0tBQ0Q7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNYLDhEQUE4RDtRQUM5RCxPQUFPLENBQUMsS0FBSyxDQUFDLGdCQUFnQixHQUFHLFVBQVUsR0FBRyxjQUFjLENBQUMsQ0FBQztLQUM5RDtJQUVELHdDQUF3QztJQUN4QyxNQUFNLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFO1FBQ3RFLE9BQU8sWUFBWSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEtBQUssS0FBSyxJQUFJLFlBQVksQ0FBQyxTQUFTLENBQUMsR0FBSSxDQUFDLElBQUksS0FBSyxVQUFVLENBQUM7SUFDakcsQ0FBQyxDQUFDLENBQUM7SUFDSCxNQUFNLGdCQUFnQixHQUFHLHVEQUF1RCxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLCtDQUErQyxDQUFDO0lBQzNKLE1BQU0sMkJBQTJCLEdBQUcsR0FBRyxJQUFBLFdBQU0sR0FBRSxvQkFBb0IsQ0FBQztJQUNwRSxNQUFNLE1BQU0sR0FBRyxJQUFBLHlCQUFTLEVBQUMsTUFBTSxFQUFFLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxFQUFFLDJCQUEyQixDQUFDLENBQUMsQ0FBQztJQUN4RixJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3hCLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0tBQzdFO0lBQ0QsTUFBTSxHQUFHLEdBQUcsQ0FBQywyQkFBMkIsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO0lBQ3JFLFFBQVEsSUFBSSxFQUFFO1FBQ2IsS0FBSyxPQUFPO1lBQ1gsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLE9BQU8sMkJBQTJCLEVBQy9DLEtBQUssT0FBTyx1QkFBdUIsQ0FBQyxDQUFDO1lBQ3RDLE1BQU07UUFDUCxLQUFLLE9BQU87WUFDWCxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssT0FBTyw4QkFBOEIsRUFDbEQsS0FBSyxPQUFPLDBCQUEwQixDQUFDLENBQUM7WUFDekMsTUFBTTtRQUNQLEtBQUssT0FBTztZQUNYLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxPQUFPLDRCQUE0QixFQUNoRCxLQUFLLE9BQU8sd0JBQXdCLENBQUMsQ0FBQztZQUN2QyxNQUFNO0tBQ1A7SUFDRCxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssT0FBTyxVQUFVLENBQUMsQ0FBQztJQUNqQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO0lBRS9DLE1BQU0sbUJBQW1CLEdBQUcsSUFBQSx5QkFBUyxFQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNyRSxJQUFJLG1CQUFtQixDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7UUFDckMsTUFBTSxJQUFJLEtBQUssQ0FBQyx3Q0FBd0MsbUJBQW1CLENBQUMsTUFBTSxjQUFjLG1CQUFtQixDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7S0FDL0g7SUFFRCxNQUFNLG1CQUFtQixHQUFHLGlCQUFpQixDQUFDO0lBQzlDLE1BQU0sWUFBWSxHQUFHLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3hGLElBQUksT0FBTyxHQUFHLEVBQUUsQ0FBQztJQUNqQixLQUFLLE1BQU0sSUFBSSxJQUFJLFlBQVksRUFBRTtRQUNoQyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsRUFBRTtZQUN6QyxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUNyRDtLQUNEO0lBQ0QseUVBQXlFO0lBQ3pFLDBFQUEwRTtJQUMxRSx5RUFBeUU7SUFDekUsdUVBQXVFO0lBQ3ZFLHFFQUFxRTtJQUNyRSwyREFBMkQ7SUFDM0QsRUFBRTtJQUNGLHFFQUFxRTtJQUNyRSx1RUFBdUU7SUFDdkUsMERBQTBEO0lBQzFELHFFQUFxRTtJQUNyRSxvREFBb0Q7SUFDcEQsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLEVBQUU7UUFDNUQsT0FBTyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDO1lBQ3pDLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQztZQUMxQyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDdEMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDVixNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN2QyxPQUFPLFFBQVEsQ0FBQztBQUNqQixDQUFDIn0= \ No newline at end of file diff --git a/build/linux/debian/calculate-deps.ts b/build/linux/debian/calculate-deps.ts index b13d3cdfaaf..a23c41ace24 100644 --- a/build/linux/debian/calculate-deps.ts +++ b/build/linux/debian/calculate-deps.ts @@ -76,8 +76,16 @@ function calculatePackageDeps(binaryPath: string, arch: DebianArchString, sysroo // on the newer package, this hack skips the dep. This is safe because // libgcc-s1 is a dependency of libc6. This hack can be removed once // support for Debian Buster and Ubuntu Bionic are dropped. + // + // Remove kerberos native module related dependencies as the versions + // computed from sysroot will not satisfy the minimum supported distros + // Refs https://github.com/microsoft/vscode/issues/188881. + // TODO(deepak1556): remove this workaround in favor of computing the + // versions from build container for native modules. const filteredDeps = depsStr.split(', ').filter(dependency => { - return !dependency.startsWith('libgcc-s1'); + return !dependency.startsWith('libgcc-s1') && + !dependency.startsWith('libgssapi-krb5-2') && + !dependency.startsWith('libkrb5-3'); }).sort(); const requires = new Set(filteredDeps); return requires; diff --git a/build/linux/debian/dep-lists.js b/build/linux/debian/dep-lists.js index 2444e401703..659d36b73b7 100644 --- a/build/linux/debian/dep-lists.js +++ b/build/linux/debian/dep-lists.js @@ -12,7 +12,9 @@ exports.additionalDeps = [ 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', 'libnss3 (>= 3.26)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', - 'xdg-utils (>= 1.0.2)' // OS integration + 'xdg-utils (>= 1.0.2)', + 'libgssapi-krb5-2', + 'libkrb5-3', ]; // Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/manual_recommends // Dependencies that we can only recommend @@ -38,8 +40,10 @@ exports.referenceGeneratedDepsByArch = { 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.16.0)', 'libglib2.0-0 (>= 2.39.4)', + 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libkrb5-3', 'libnspr4 (>= 2:4.9-2~)', 'libnss3 (>= 2:3.22)', 'libnss3 (>= 3.26)', @@ -76,8 +80,10 @@ exports.referenceGeneratedDepsByArch = { 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.39.4)', + 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libkrb5-3', 'libnspr4 (>= 2:4.9-2~)', 'libnss3 (>= 2:3.22)', 'libnss3 (>= 3.26)', @@ -113,8 +119,10 @@ exports.referenceGeneratedDepsByArch = { 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.39.4)', + 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libkrb5-3', 'libnspr4 (>= 2:4.9-2~)', 'libnss3 (>= 2:3.22)', 'libnss3 (>= 3.26)', @@ -136,4 +144,4 @@ exports.referenceGeneratedDepsByArch = { 'xdg-utils (>= 1.0.2)' ] }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQixDQUFDLGlCQUFpQjtDQUN4QyxDQUFDO0FBRUYsb0hBQW9IO0FBQ3BILDBDQUEwQztBQUMxQyw4REFBOEQ7QUFDakQsUUFBQSxlQUFlLEdBQUc7SUFDOUIsWUFBWSxDQUFDLHlFQUF5RTtDQUN0RixDQUFDO0FBRVcsUUFBQSw0QkFBNEIsR0FBRztJQUMzQyxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQiw0QkFBNEI7UUFDNUIseUJBQXlCO1FBQ3pCLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQix3QkFBd0I7UUFDeEIscUNBQXFDO1FBQ3JDLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1Qix5QkFBeUI7UUFDekIsbUJBQW1CO1FBQ25CLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtJQUNELE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQiw0QkFBNEI7UUFDNUIseUJBQXlCO1FBQ3pCLG1CQUFtQjtRQUNuQixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7Q0FDRCxDQUFDIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsV0FBVztDQUNYLENBQUM7QUFFRixvSEFBb0g7QUFDcEgsMENBQTBDO0FBQzFDLDhEQUE4RDtBQUNqRCxRQUFBLGVBQWUsR0FBRztJQUM5QixZQUFZLENBQUMseUVBQXlFO0NBQ3RGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHO0lBQzNDLE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixrQkFBa0I7UUFDbEIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6QixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0lBQ0QsT0FBTyxFQUFFO1FBQ1IsaUJBQWlCO1FBQ2pCLHdCQUF3QjtRQUN4QiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLDJCQUEyQjtRQUMzQixpQkFBaUI7UUFDakIsaUJBQWlCO1FBQ2pCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsZ0JBQWdCO1FBQ2hCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1Qix5QkFBeUI7UUFDekIsbUJBQW1CO1FBQ25CLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtJQUNELE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1Qix5QkFBeUI7UUFDekIsbUJBQW1CO1FBQ25CLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtDQUNELENBQUMifQ== \ No newline at end of file diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts index 7f6cd6ca8cc..aace840a29b 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts @@ -10,7 +10,9 @@ export const additionalDeps = [ 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', 'libnss3 (>= 3.26)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', // For Breakpad crash reports. - 'xdg-utils (>= 1.0.2)' // OS integration + 'xdg-utils (>= 1.0.2)', // OS integration + 'libgssapi-krb5-2', + 'libkrb5-3', ]; // Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/manual_recommends @@ -38,8 +40,10 @@ export const referenceGeneratedDepsByArch = { 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.16.0)', 'libglib2.0-0 (>= 2.39.4)', + 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libkrb5-3', 'libnspr4 (>= 2:4.9-2~)', 'libnss3 (>= 2:3.22)', 'libnss3 (>= 3.26)', @@ -76,8 +80,10 @@ export const referenceGeneratedDepsByArch = { 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.39.4)', + 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libkrb5-3', 'libnspr4 (>= 2:4.9-2~)', 'libnss3 (>= 2:3.22)', 'libnss3 (>= 3.26)', @@ -113,8 +119,10 @@ export const referenceGeneratedDepsByArch = { 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.39.4)', + 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libkrb5-3', 'libnspr4 (>= 2:4.9-2~)', 'libnss3 (>= 2:3.22)', 'libnss3 (>= 3.26)', diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index c836348ef51..e6b9a389064 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -65,7 +65,11 @@ exports.referenceGeneratedDepsByArch = { 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', + 'libgssapi_krb5.so.2()(64bit)', + 'libgssapi_krb5.so.2(gssapi_krb5_2_MIT)(64bit)', 'libgtk-3.so.0()(64bit)', + 'libkrb5.so.3()(64bit)', + 'libkrb5.so.3(krb5_3_MIT)(64bit)', 'libm.so.6()(64bit)', 'libm.so.6(GLIBC_2.2.5)(64bit)', 'libnspr4.so()(64bit)', @@ -126,7 +130,6 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.15)', 'libc.so.6(GLIBC_2.16)', 'libc.so.6(GLIBC_2.17)', - 'libc.so.6(GLIBC_2.25)', 'libc.so.6(GLIBC_2.4)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', @@ -146,8 +149,12 @@ exports.referenceGeneratedDepsByArch = { 'libgio-2.0.so.0', 'libglib-2.0.so.0', 'libgobject-2.0.so.0', + 'libgssapi_krb5.so.2', + 'libgssapi_krb5.so.2(gssapi_krb5_2_MIT)', 'libgtk-3.so.0', 'libgtk-3.so.0()(64bit)', + 'libkrb5.so.3', + 'libkrb5.so.3(krb5_3_MIT)', 'libm.so.6', 'libm.so.6(GLIBC_2.4)', 'libnspr4.so', @@ -219,7 +226,6 @@ exports.referenceGeneratedDepsByArch = { 'libatspi.so.0()(64bit)', 'libc.so.6()(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)', - 'libc.so.6(GLIBC_2.25)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', 'libdbus-1.so.3()(64bit)', @@ -236,7 +242,11 @@ exports.referenceGeneratedDepsByArch = { 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', + 'libgssapi_krb5.so.2()(64bit)', + 'libgssapi_krb5.so.2(gssapi_krb5_2_MIT)(64bit)', 'libgtk-3.so.0()(64bit)', + 'libkrb5.so.3()(64bit)', + 'libkrb5.so.3(krb5_3_MIT)(64bit)', 'libm.so.6()(64bit)', 'libm.so.6(GLIBC_2.17)(64bit)', 'libnspr4.so()(64bit)', @@ -289,4 +299,4 @@ exports.referenceGeneratedDepsByArch = { 'xdg-utils' ] }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLCtHQUErRztBQUMvRywrREFBK0Q7QUFDbEQsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHdCQUF3QjtJQUN4Qiw2QkFBNkI7SUFDN0IsNkJBQTZCO0lBQzdCLGdDQUFnQztJQUNoQyx5QkFBeUI7SUFDekIsdUJBQXVCO0lBQ3ZCLFdBQVcsQ0FBQyxpQkFBaUI7Q0FDN0IsQ0FBQztBQUVXLFFBQUEsNEJBQTRCLEdBQUc7SUFDM0MsUUFBUSxFQUFFO1FBQ1QsaUJBQWlCO1FBQ2pCLCtCQUErQjtRQUMvQiwwQ0FBMEM7UUFDMUMsd0NBQXdDO1FBQ3hDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0Isd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLDBCQUEwQjtRQUMxQiwyQkFBMkI7UUFDM0IsOEJBQThCO1FBQzlCLHdCQUF3QjtRQUN4QixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHNCQUFzQjtRQUN0QixxQkFBcUI7UUFDckIsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3QiwrQkFBK0I7UUFDL0IsNEJBQTRCO1FBQzVCLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsNEJBQTRCO1FBQzVCLDRCQUE0QjtRQUM1Qiw4QkFBOEI7UUFDOUIseUJBQXlCO1FBQ3pCLHVDQUF1QztRQUN2Qyw0QkFBNEI7UUFDNUIsMEJBQTBCO1FBQzFCLG9DQUFvQztRQUNwQyxxQ0FBcUM7UUFDckMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQywyQkFBMkI7UUFDM0IsdUJBQXVCO1FBQ3ZCLCtCQUErQjtRQUMvQiw4QkFBOEI7UUFDOUIsNkJBQTZCO1FBQzdCLHVCQUF1QjtRQUN2QixrQ0FBa0M7UUFDbEMsc0JBQXNCO1FBQ3RCLDRCQUE0QjtRQUM1QiwwQkFBMEI7UUFDMUIsZ0NBQWdDO1FBQ2hDLGdCQUFnQjtRQUNoQixXQUFXO0tBQ1g7SUFDRCxTQUFTLEVBQUU7UUFDVixpQkFBaUI7UUFDakIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxhQUFhO1FBQ2Isb0JBQW9CO1FBQ3BCLGlCQUFpQjtRQUNqQixjQUFjO1FBQ2QsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsMEJBQTBCO1FBQzFCLCtCQUErQjtRQUMvQixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLGVBQWU7UUFDZixXQUFXO1FBQ1gsdUJBQXVCO1FBQ3ZCLHVCQUF1QjtRQUN2Qix1QkFBdUI7UUFDdkIsdUJBQXVCO1FBQ3ZCLHVCQUF1QjtRQUN2Qix1QkFBdUI7UUFDdkIsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0QixlQUFlO1FBQ2YsdUJBQXVCO1FBQ3ZCLGdCQUFnQjtRQUNoQixZQUFZO1FBQ1osdUJBQXVCO1FBQ3ZCLGFBQWE7UUFDYixlQUFlO1FBQ2YsYUFBYTtRQUNiLGVBQWU7UUFDZix3QkFBd0I7UUFDeEIsd0JBQXdCO1FBQ3hCLGlCQUFpQjtRQUNqQixrQkFBa0I7UUFDbEIscUJBQXFCO1FBQ3JCLGVBQWU7UUFDZix3QkFBd0I7UUFDeEIsV0FBVztRQUNYLHNCQUFzQjtRQUN0QixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIsNkJBQTZCO1FBQzdCLHFCQUFxQjtRQUNyQixxQkFBcUI7UUFDckIscUJBQXFCO1FBQ3JCLHVCQUF1QjtRQUN2QixnQkFBZ0I7UUFDaEIsZ0NBQWdDO1FBQ2hDLG1CQUFtQjtRQUNuQixpQkFBaUI7UUFDakIsNkJBQTZCO1FBQzdCLDRCQUE0QjtRQUM1QixZQUFZO1FBQ1osdUJBQXVCO1FBQ3ZCLGtCQUFrQjtRQUNsQixjQUFjO1FBQ2Qsd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qiw2QkFBNkI7UUFDN0IsZ0JBQWdCO1FBQ2hCLDRCQUE0QjtRQUM1Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QixrQ0FBa0M7UUFDbEMsNkJBQTZCO1FBQzdCLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsK0JBQStCO1FBQy9CLCtCQUErQjtRQUMvQixjQUFjO1FBQ2QseUJBQXlCO1FBQ3pCLGFBQWE7UUFDYixtQkFBbUI7UUFDbkIsaUJBQWlCO1FBQ2pCLGdDQUFnQztRQUNoQyxnQkFBZ0I7UUFDaEIsV0FBVztLQUNYO0lBQ0QsU0FBUyxFQUFFO1FBQ1YsaUJBQWlCO1FBQ2pCLGdDQUFnQztRQUNoQywwQ0FBMEM7UUFDMUMsc0JBQXNCO1FBQ3RCLDZCQUE2QjtRQUM3QiwwQkFBMEI7UUFDMUIsdUJBQXVCO1FBQ3ZCLHlCQUF5QjtRQUN6Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLGlDQUFpQztRQUNqQyxzQ0FBc0M7UUFDdEMsMEJBQTBCO1FBQzFCLGlDQUFpQztRQUNqQyx3QkFBd0I7UUFDeEIsb0JBQW9CO1FBQ3BCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIsb0NBQW9DO1FBQ3BDLHFCQUFxQjtRQUNyQiwrQkFBK0I7UUFDL0Isc0JBQXNCO1FBQ3RCLHdCQUF3QjtRQUN4QixzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQixpQ0FBaUM7UUFDakMsaUNBQWlDO1FBQ2pDLDBCQUEwQjtRQUMxQiwyQkFBMkI7UUFDM0IsOEJBQThCO1FBQzlCLHdCQUF3QjtRQUN4QixvQkFBb0I7UUFDcEIsOEJBQThCO1FBQzlCLHNCQUFzQjtRQUN0QixxQkFBcUI7UUFDckIsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3QiwrQkFBK0I7UUFDL0IsNEJBQTRCO1FBQzVCLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsNEJBQTRCO1FBQzVCLDRCQUE0QjtRQUM1Qiw4QkFBOEI7UUFDOUIseUJBQXlCO1FBQ3pCLHVDQUF1QztRQUN2Qyw0QkFBNEI7UUFDNUIsMEJBQTBCO1FBQzFCLG9DQUFvQztRQUNwQyxxQkFBcUI7UUFDckIsK0JBQStCO1FBQy9CLDJCQUEyQjtRQUMzQix1QkFBdUI7UUFDdkIsK0JBQStCO1FBQy9CLDhCQUE4QjtRQUM5Qiw2QkFBNkI7UUFDN0IseUJBQXlCO1FBQ3pCLG1DQUFtQztRQUNuQyxxQ0FBcUM7UUFDckMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxvQ0FBb0M7UUFDcEMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2QyxzQ0FBc0M7UUFDdEMsc0NBQXNDO1FBQ3RDLHVCQUF1QjtRQUN2QixpQ0FBaUM7UUFDakMsc0JBQXNCO1FBQ3RCLDRCQUE0QjtRQUM1QixtQ0FBbUM7UUFDbkMsMEJBQTBCO1FBQzFCLGdDQUFnQztRQUNoQyxnQkFBZ0I7UUFDaEIsV0FBVztLQUNYO0NBQ0QsQ0FBQyJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLCtHQUErRztBQUMvRywrREFBK0Q7QUFDbEQsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHdCQUF3QjtJQUN4Qiw2QkFBNkI7SUFDN0IsNkJBQTZCO0lBQzdCLGdDQUFnQztJQUNoQyx5QkFBeUI7SUFDekIsdUJBQXVCO0lBQ3ZCLFdBQVcsQ0FBQyxpQkFBaUI7Q0FDN0IsQ0FBQztBQUVXLFFBQUEsNEJBQTRCLEdBQUc7SUFDM0MsUUFBUSxFQUFFO1FBQ1QsaUJBQWlCO1FBQ2pCLCtCQUErQjtRQUMvQiwwQ0FBMEM7UUFDMUMsd0NBQXdDO1FBQ3hDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0Isd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLDBCQUEwQjtRQUMxQiwyQkFBMkI7UUFDM0IsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQ0FBK0M7UUFDL0Msd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2QixpQ0FBaUM7UUFDakMsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQixzQkFBc0I7UUFDdEIscUJBQXFCO1FBQ3JCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsK0JBQStCO1FBQy9CLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLDRCQUE0QjtRQUM1Qiw0QkFBNEI7UUFDNUIsOEJBQThCO1FBQzlCLHlCQUF5QjtRQUN6Qix1Q0FBdUM7UUFDdkMsNEJBQTRCO1FBQzVCLDBCQUEwQjtRQUMxQixvQ0FBb0M7UUFDcEMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMscUNBQXFDO1FBQ3JDLHFCQUFxQjtRQUNyQixnQ0FBZ0M7UUFDaEMsMkJBQTJCO1FBQzNCLHVCQUF1QjtRQUN2QiwrQkFBK0I7UUFDL0IsOEJBQThCO1FBQzlCLDZCQUE2QjtRQUM3Qix1QkFBdUI7UUFDdkIsa0NBQWtDO1FBQ2xDLHNCQUFzQjtRQUN0Qiw0QkFBNEI7UUFDNUIsMEJBQTBCO1FBQzFCLGdDQUFnQztRQUNoQyxnQkFBZ0I7UUFDaEIsV0FBVztLQUNYO0lBQ0QsU0FBUyxFQUFFO1FBQ1YsaUJBQWlCO1FBQ2pCLHFCQUFxQjtRQUNyQixnQ0FBZ0M7UUFDaEMsYUFBYTtRQUNiLG9CQUFvQjtRQUNwQixpQkFBaUI7UUFDakIsY0FBYztRQUNkLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsZ0JBQWdCO1FBQ2hCLDBCQUEwQjtRQUMxQiwrQkFBK0I7UUFDL0IsaUJBQWlCO1FBQ2pCLHdCQUF3QjtRQUN4QixlQUFlO1FBQ2YsV0FBVztRQUNYLHVCQUF1QjtRQUN2Qix1QkFBdUI7UUFDdkIsdUJBQXVCO1FBQ3ZCLHVCQUF1QjtRQUN2Qix1QkFBdUI7UUFDdkIsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0QixlQUFlO1FBQ2YsdUJBQXVCO1FBQ3ZCLGdCQUFnQjtRQUNoQixZQUFZO1FBQ1osdUJBQXVCO1FBQ3ZCLGFBQWE7UUFDYixlQUFlO1FBQ2YsYUFBYTtRQUNiLGVBQWU7UUFDZix3QkFBd0I7UUFDeEIsd0JBQXdCO1FBQ3hCLGlCQUFpQjtRQUNqQixrQkFBa0I7UUFDbEIscUJBQXFCO1FBQ3JCLHFCQUFxQjtRQUNyQix3Q0FBd0M7UUFDeEMsZUFBZTtRQUNmLHdCQUF3QjtRQUN4QixjQUFjO1FBQ2QsMEJBQTBCO1FBQzFCLFdBQVc7UUFDWCxzQkFBc0I7UUFDdEIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7UUFDdEIsc0JBQXNCO1FBQ3RCLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLDZCQUE2QjtRQUM3QixxQkFBcUI7UUFDckIscUJBQXFCO1FBQ3JCLHFCQUFxQjtRQUNyQix1QkFBdUI7UUFDdkIsZ0JBQWdCO1FBQ2hCLGdDQUFnQztRQUNoQyxtQkFBbUI7UUFDbkIsaUJBQWlCO1FBQ2pCLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsWUFBWTtRQUNaLHVCQUF1QjtRQUN2QixrQkFBa0I7UUFDbEIsY0FBYztRQUNkLHdCQUF3QjtRQUN4Qix1QkFBdUI7UUFDdkIsNkJBQTZCO1FBQzdCLGdCQUFnQjtRQUNoQiw0QkFBNEI7UUFDNUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsa0NBQWtDO1FBQ2xDLDZCQUE2QjtRQUM3QixnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsY0FBYztRQUNkLHlCQUF5QjtRQUN6QixhQUFhO1FBQ2IsbUJBQW1CO1FBQ25CLGlCQUFpQjtRQUNqQixnQ0FBZ0M7UUFDaEMsZ0JBQWdCO1FBQ2hCLFdBQVc7S0FDWDtJQUNELFNBQVMsRUFBRTtRQUNWLGlCQUFpQjtRQUNqQixnQ0FBZ0M7UUFDaEMsMENBQTBDO1FBQzFDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIsb0NBQW9DO1FBQ3BDLHFCQUFxQjtRQUNyQiwrQkFBK0I7UUFDL0Isc0JBQXNCO1FBQ3RCLHdCQUF3QjtRQUN4QixzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQixpQ0FBaUM7UUFDakMsaUNBQWlDO1FBQ2pDLDBCQUEwQjtRQUMxQiwyQkFBMkI7UUFDM0IsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQ0FBK0M7UUFDL0Msd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2QixpQ0FBaUM7UUFDakMsb0JBQW9CO1FBQ3BCLDhCQUE4QjtRQUM5QixzQkFBc0I7UUFDdEIscUJBQXFCO1FBQ3JCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsK0JBQStCO1FBQy9CLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLDRCQUE0QjtRQUM1Qiw0QkFBNEI7UUFDNUIsOEJBQThCO1FBQzlCLHlCQUF5QjtRQUN6Qix1Q0FBdUM7UUFDdkMsNEJBQTRCO1FBQzVCLDBCQUEwQjtRQUMxQixvQ0FBb0M7UUFDcEMscUJBQXFCO1FBQ3JCLCtCQUErQjtRQUMvQiwyQkFBMkI7UUFDM0IsdUJBQXVCO1FBQ3ZCLCtCQUErQjtRQUMvQiw4QkFBOEI7UUFDOUIsNkJBQTZCO1FBQzdCLHlCQUF5QjtRQUN6QixtQ0FBbUM7UUFDbkMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMsb0NBQW9DO1FBQ3BDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsc0NBQXNDO1FBQ3RDLHNDQUFzQztRQUN0Qyx1QkFBdUI7UUFDdkIsaUNBQWlDO1FBQ2pDLHNCQUFzQjtRQUN0Qiw0QkFBNEI7UUFDNUIsbUNBQW1DO1FBQ25DLDBCQUEwQjtRQUMxQixnQ0FBZ0M7UUFDaEMsZ0JBQWdCO1FBQ2hCLFdBQVc7S0FDWDtDQUNELENBQUMifQ== \ No newline at end of file diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index c262448c318..2424efc6cb8 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -64,7 +64,11 @@ export const referenceGeneratedDepsByArch = { 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', + 'libgssapi_krb5.so.2()(64bit)', + 'libgssapi_krb5.so.2(gssapi_krb5_2_MIT)(64bit)', 'libgtk-3.so.0()(64bit)', + 'libkrb5.so.3()(64bit)', + 'libkrb5.so.3(krb5_3_MIT)(64bit)', 'libm.so.6()(64bit)', 'libm.so.6(GLIBC_2.2.5)(64bit)', 'libnspr4.so()(64bit)', @@ -125,7 +129,6 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.15)', 'libc.so.6(GLIBC_2.16)', 'libc.so.6(GLIBC_2.17)', - 'libc.so.6(GLIBC_2.25)', 'libc.so.6(GLIBC_2.4)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', @@ -145,8 +148,12 @@ export const referenceGeneratedDepsByArch = { 'libgio-2.0.so.0', 'libglib-2.0.so.0', 'libgobject-2.0.so.0', + 'libgssapi_krb5.so.2', + 'libgssapi_krb5.so.2(gssapi_krb5_2_MIT)', 'libgtk-3.so.0', 'libgtk-3.so.0()(64bit)', + 'libkrb5.so.3', + 'libkrb5.so.3(krb5_3_MIT)', 'libm.so.6', 'libm.so.6(GLIBC_2.4)', 'libnspr4.so', @@ -218,7 +225,6 @@ export const referenceGeneratedDepsByArch = { 'libatspi.so.0()(64bit)', 'libc.so.6()(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)', - 'libc.so.6(GLIBC_2.25)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', 'libdbus-1.so.3()(64bit)', @@ -235,7 +241,11 @@ export const referenceGeneratedDepsByArch = { 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', + 'libgssapi_krb5.so.2()(64bit)', + 'libgssapi_krb5.so.2(gssapi_krb5_2_MIT)(64bit)', 'libgtk-3.so.0()(64bit)', + 'libkrb5.so.3()(64bit)', + 'libkrb5.so.3(krb5_3_MIT)(64bit)', 'libm.so.6()(64bit)', 'libm.so.6(GLIBC_2.17)(64bit)', 'libnspr4.so()(64bit)', diff --git a/build/npm/gyp/package.json b/build/npm/gyp/package.json index 0efaa499b9d..3961e955a5f 100644 --- a/build/npm/gyp/package.json +++ b/build/npm/gyp/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "devDependencies": { - "node-gyp": "^8.4.1" + "node-gyp": "^9.4.0" }, "scripts": {} } diff --git a/build/npm/gyp/patches/gyp_spectre_mitigation_support.patch b/build/npm/gyp/patches/gyp_spectre_mitigation_support.patch new file mode 100644 index 00000000000..e64f42e2b04 --- /dev/null +++ b/build/npm/gyp/patches/gyp_spectre_mitigation_support.patch @@ -0,0 +1,51 @@ +From 853e4643b6737224a5aa0720a4108461a0230991 Mon Sep 17 00:00:00 2001 +From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> +Date: Thu, 30 Mar 2023 05:23:36 -0700 +Subject: [PATCH] feat(msvs): add SpectreMitigation attribute (#190) + +Backports https://github.com/nodejs/gyp-next/commit/853e4643b6737224a5aa0720a4108461a0230991 + +diff --git a/gyp/pylib/gyp/easy_xml_test.py b/gyp/pylib/gyp/easy_xml_test.py +index 342f693..c5808b8 100755 +--- a/gyp/pylib/gyp/easy_xml_test.py ++++ b/gyp/pylib/gyp/easy_xml_test.py +@@ -76,6 +76,7 @@ def test_EasyXml_complex(self): + '\'Debug|Win32\'" Label="Configuration">' + "Application" + "Unicode" ++ "SpectreLoadCF" + "" + "" + ) +@@ -99,6 +100,7 @@ def test_EasyXml_complex(self): + }, + ["ConfigurationType", "Application"], + ["CharacterSet", "Unicode"], ++ ["SpectreMitigation", "SpectreLoadCF"] + ], + ] + ) +diff --git a/gyp/pylib/gyp/generator/msvs.py b/gyp/pylib/gyp/generator/msvs.py +index 72269bd..85c354f 100644 +--- a/gyp/pylib/gyp/generator/msvs.py ++++ b/gyp/pylib/gyp/generator/msvs.py +@@ -3006,6 +3006,10 @@ def _GetMSBuildConfigurationDetails(spec, build_file): + character_set = msbuild_attributes.get("CharacterSet") + config_type = msbuild_attributes.get("ConfigurationType") + _AddConditionalProperty(properties, condition, "ConfigurationType", config_type) ++ spectre_mitigation = msbuild_attributes.get('SpectreMitigation') ++ if spectre_mitigation: ++ _AddConditionalProperty(properties, condition, "SpectreMitigation", ++ spectre_mitigation) + if config_type == "Driver": + _AddConditionalProperty(properties, condition, "DriverType", "WDM") + _AddConditionalProperty( +@@ -3094,6 +3098,8 @@ def _ConvertMSVSBuildAttributes(spec, config, build_file): + msbuild_attributes[a] = _ConvertMSVSCharacterSet(msvs_attributes[a]) + elif a == "ConfigurationType": + msbuild_attributes[a] = _ConvertMSVSConfigurationType(msvs_attributes[a]) ++ elif a == "SpectreMitigation": ++ msbuild_attributes[a] = msvs_attributes[a] + else: + print("Warning: Do not know how to convert MSVS attribute " + a) + return msbuild_attributes diff --git a/build/npm/gyp/yarn.lock b/build/npm/gyp/yarn.lock index d5d6bced114..3716071611c 100644 --- a/build/npm/gyp/yarn.lock +++ b/build/npm/gyp/yarn.lock @@ -2,33 +2,36 @@ # yarn lockfile v1 -"@gar/promisify@^1.0.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - -"@npmcli/fs@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" - integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== dependencies: - "@gar/promisify" "^1.0.1" semver "^7.3.5" -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -abbrev@1: +abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -40,13 +43,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agentkeepalive@^4.1.3: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== +agentkeepalive@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" + integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== dependencies: debug "^4.1.0" - depd "^1.1.2" + depd "^2.0.0" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -62,6 +65,23 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + "aproba@^1.0.3 || ^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" @@ -88,29 +108,30 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -cacache@^15.2.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" - integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: - "@npmcli/fs" "^1.0.0" - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" + balanced-match "^1.0.0" + +cacache@^17.0.0: + version "17.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.3.tgz#c6ac23bec56516a7c0c52020fd48b4909d7c7044" + integrity sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^7.7.1" + minipass "^5.0.0" minipass-collect "^1.0.2" minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" + minipass-pipeline "^1.2.4" p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" chownr@^2.0.0: version "2.0.0" @@ -122,6 +143,18 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -137,6 +170,15 @@ console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + debug@4, debug@^4.1.0, debug@^4.3.3: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -149,17 +191,27 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -depd@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +depd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -encoding@^0.1.12: +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -176,6 +228,19 @@ err-code@^2.0.2: resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -183,6 +248,13 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.2.tgz#5b383858efa8c1eb8c33b39e994f7e8555b8b3a3" + integrity sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g== + dependencies: + minipass "^5.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -202,6 +274,17 @@ gauge@^4.0.3: strip-ansi "^6.0.1" wide-align "^1.1.5" +glob@^10.2.2: + version "10.3.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" + integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -224,17 +307,17 @@ has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: - "@tootallnate/once" "1" + "@tootallnate/once" "2" agent-base "6" debug "4" @@ -270,11 +353,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -308,6 +386,15 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +jackspeak@^2.0.3: + version "2.2.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.2.tgz#707c62733924b8dc2a0a629dc6248577788b5385" + integrity sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -315,27 +402,36 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-fetch-happen@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" - integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== +lru-cache@^7.7.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" + integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== + +make-fetch-happen@^11.0.3: + version "11.1.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" + lru-cache "^7.7.1" + minipass "^5.0.0" + minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" - negotiator "^0.6.2" + negotiator "^0.6.3" promise-retry "^2.0.1" - socks-proxy-agent "^6.0.0" - ssri "^8.0.0" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" minimatch@^3.1.1: version "3.1.2" @@ -344,6 +440,13 @@ minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -351,16 +454,16 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" - integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== +minipass-fetch@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.3.tgz#d9df70085609864331b533c960fd4ffaa78d15ce" + integrity sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ== dependencies: - minipass "^3.1.0" + minipass "^5.0.0" minipass-sized "^1.0.3" - minizlib "^2.0.0" + minizlib "^2.1.2" optionalDependencies: - encoding "^0.1.12" + encoding "^0.1.13" minipass-flush@^1.0.5: version "1.0.5" @@ -369,7 +472,7 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: +minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== @@ -383,7 +486,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: +minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -395,7 +498,17 @@ minipass@^4.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.3.tgz#00bfbaf1e16e35e804f4aa31a7c1f6b8d9f0ee72" integrity sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw== -minizlib@^2.0.0, minizlib@^2.1.1: +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" + integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== + +minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -403,7 +516,7 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -418,33 +531,34 @@ ms@^2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -negotiator@^0.6.2: +negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -node-gyp@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" - integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== +node-gyp@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" + integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== dependencies: env-paths "^2.2.0" + exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^9.1.0" - nopt "^5.0.0" + make-fetch-happen "^11.0.3" + nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" semver "^7.3.5" tar "^6.1.2" which "^2.0.2" -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== +nopt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" + integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== dependencies: - abbrev "1" + abbrev "^1.0.0" npmlog@^6.0.0: version "6.0.2" @@ -475,10 +589,18 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" promise-retry@^2.0.1: version "2.0.1" @@ -520,9 +642,9 @@ safe-buffer@~5.2.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== semver@^7.3.5: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -531,20 +653,37 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -socks-proxy-agent@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" - integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== dependencies: agent-base "^6.0.2" debug "^4.3.3" @@ -558,14 +697,14 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" -ssri@^8.0.0, ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== +ssri@^10.0.0: + version "10.0.4" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.4.tgz#5a20af378be586df139ddb2dfb3bf992cf0daba6" + integrity sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ== dependencies: - minipass "^3.1.1" + minipass "^5.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -574,6 +713,15 @@ ssri@^8.0.0, ssri@^8.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -581,14 +729,33 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -tar@^6.0.2, tar@^6.1.2: +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +tar@^6.1.11: + version "6.1.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" + integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +tar@^6.1.2: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -600,17 +767,17 @@ tar@^6.0.2, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== dependencies: - unique-slug "^2.0.0" + unique-slug "^4.0.0" -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== dependencies: imurmurhash "^0.1.4" @@ -619,7 +786,7 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -633,6 +800,24 @@ wide-align@^1.1.5: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index d9280ffb1eb..09df602a3bf 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -53,7 +53,10 @@ function yarnInstall(dir, opts) { console.log(`Installing dependencies in ${dir} inside container ${process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME']}...`); opts.cwd = root; - run('docker', ['run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `/mnt/vss/_work/1/s:/root/vscode`, '-v', `/mnt/vss/_work/1/s/.build/.netrc:/root/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts); + if (process.env['npm_config_arch'] === 'arm64') { + run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], opts); + } + run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/root/vscode`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/root/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts); run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${dir}/node_modules`], opts); } else { console.log(`Installing dependencies in ${dir}...`); diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index 79f2b629c5d..31d2a6d5c30 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -123,7 +123,19 @@ function installHeaders() { cp.execFileSync(node_gyp, ['install', '--dist-url', local.disturl, local.target]); } - if (remote !== undefined && !versions.has(remote.target)) { + // Avoid downloading headers for Windows arm64 till we move to Nodejs v19 in remote + // which is the first official release with support for the architecture. Downloading + // the headers for older versions now redirect to https://origin.nodejs.org/404.html + // which causes checksum validation error in node-gyp. + // + // gyp http 200 https://origin.nodejs.org/404.html + // gyp WARN install got an error, rolling back install + // gyp ERR! install error + // gyp ERR! stack Error: win-arm64/node.lib local checksum 4c62bed7a032f7b36984321b7ffdd60b596fac870672037ff879ae9ac9548fb7 not match remote undefined + // + if (remote !== undefined && !versions.has(remote.target) && + process.env['npm_config_arch'] !== "arm64" && + process.arch !== "arm64") { // Both disturl and target come from a file checked into our repository cp.execFileSync(node_gyp, ['install', '--dist-url', remote.disturl, remote.target]); } diff --git a/build/package.json b/build/package.json index df7b7799a24..b1b42569425 100644 --- a/build/package.json +++ b/build/package.json @@ -7,7 +7,6 @@ "@azure/identity": "^3.1.3", "@azure/storage-blob": "^12.13.0", "@electron/get": "^1.12.4", - "@iarna/toml": "^2.2.5", "@types/ansi-colors": "^3.2.0", "@types/byline": "^4.2.32", "@types/cssnano": "^4.0.0", @@ -24,7 +23,6 @@ "@types/gulp-postcss": "^8.0.0", "@types/gulp-rename": "^0.0.33", "@types/gulp-sourcemaps": "^0.0.32", - "@types/iarna__toml": "^2.0.2", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.1", diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index 0601c70fb9e..fb521755690 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -19,12 +19,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "build_const" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" - [[package]] name = "byteorder" version = "1.4.3" @@ -39,13 +33,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crc" -version = "1.8.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ - "build_const", + "crc-catalog", ] +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crossbeam-channel" version = "0.5.5" @@ -109,14 +109,14 @@ dependencies = [ [[package]] name = "inno_updater" -version = "0.9.0" +version = "0.10.1" dependencies = [ "byteorder", "crc", "slog", "slog-async", "slog-term", - "winapi", + "windows-sys", ] [[package]] @@ -329,3 +329,60 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index decae65f9e6..cf3cc9de80b 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,18 +1,30 @@ [package] name = "inno_updater" -version = "0.9.0" +version = "0.10.1" authors = ["Microsoft "] build = "build.rs" [dependencies] -byteorder = "1" -crc = "^1.0.0" -slog = "2.1.1" -slog-async = "2.2.0" -slog-term = "2.3.0" +byteorder = "1.4.3" +crc = "3.0.1" +slog = "2.7.0" +slog-async = "2.7.0" +slog-term = "2.9.0" -[target.'cfg(windows)'.dependencies] -winapi = { version = "^0.3.9", features = ["winuser", "libloaderapi", "commctrl", "processthreadsapi", "tlhelp32", "handleapi", "psapi", "errhandlingapi", "winbase", "shellapi"] } +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.42" +features = [ + "Win32_Foundation", + "Win32_System_Shutdown", + "Win32_UI_WindowsAndMessaging", + "Win32_System_Threading", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", + "Win32_Storage_FileSystem", + "Win32_Security", + "Win32_System_ProcessStatus", + "Win32_System_Diagnostics_ToolHelp" +] [profile.release] lto = true diff --git a/build/win32/code.iss b/build/win32/code.iss index 44c9f2f1f0b..b7336831374 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1362,12 +1362,6 @@ begin end; end; - if IsNotBackgroundUpdate() and CheckForMutexes('{#TunnelMutex}') then - begin - MsgBox('{#NameShort} is still running a tunnel. Please stop the tunnel before installing.', mbInformation, MB_OK); - Result := false - end; - end; function WizardNotSilent(): Boolean; @@ -1380,6 +1374,31 @@ end; var ShouldRestartTunnelService: Boolean; +function StopTunnelOtherProcesses(): Boolean; +var + WaitCounter: Integer; + TaskKilled: Integer; +begin + Log('Stopping all tunnel services (at ' + ExpandConstant('"{app}\bin\{#TunnelApplicationName}.exe"') + ')'); + ShellExec('', 'powershell.exe', '-Command "Get-WmiObject Win32_Process | Where-Object { $_.ExecutablePath -eq ' + ExpandConstant('''{app}\bin\{#TunnelApplicationName}.exe''') + ' } | Select @{Name=''Id''; Expression={$_.ProcessId}} | Stop-Process -Force"', '', SW_HIDE, ewWaitUntilTerminated, TaskKilled) + + WaitCounter := 10; + while (WaitCounter > 0) and CheckForMutexes('{#TunnelMutex}') do + begin + Log('Tunnel process is is still running, waiting'); + Sleep(500); + WaitCounter := WaitCounter - 1 + end; + + if CheckForMutexes('{#TunnelMutex}') then + begin + Log('Unable to stop tunnel processes'); + Result := False; + end + else + Result := True; +end; + procedure StopTunnelServiceIfNeeded(); var StopServiceResultCode: Integer; @@ -1413,7 +1432,11 @@ function PrepareToInstall(var NeedsRestart: Boolean): String; begin if IsNotBackgroundUpdate() then StopTunnelServiceIfNeeded(); - Result := '' + + if IsNotBackgroundUpdate() and not StopTunnelOtherProcesses() then + Result := '{#NameShort} is still running a tunnel process. Please stop the tunnel before installing.' + else + Result := ''; end; // VS Code will create a flag file before the update starts (/update=C:\foo\bar) @@ -1607,4 +1630,4 @@ begin #endif Exec(ExpandConstant('{sys}\icacls.exe'), ExpandConstant('"{app}" /inheritancelevel:r ') + Permissions, '', SW_HIDE, ewWaitUntilTerminated, ResultCode); -end; \ No newline at end of file +end; diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index fea47a59c9d..fa2fd26a466 100644 Binary files a/build/win32/inno_updater.exe and b/build/win32/inno_updater.exe differ diff --git a/build/yarn.lock b/build/yarn.lock index c29bf14c70a..66ff004f40b 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -339,11 +339,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.14.tgz#e81fb49de05fed91bf74251c9ca0343f4fc77d31" integrity sha512-gPQmsi2DKTaEgG14hc3CHXHp62k8g6qr0Pas+I4lUxRMugGSATh/Bi8Dgusoz9IQ0IfdrvLpco6kujEIBoaogA== -"@iarna/toml@^2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" - integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== - "@malept/cross-spawn-promise@^1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" @@ -506,13 +501,6 @@ "@types/undertaker" "*" "@types/vinyl-fs" "*" -"@types/iarna__toml@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/iarna__toml/-/iarna__toml-2.0.2.tgz#2e61b079e50760b477bc70e4df1fe5b633ef6c63" - integrity sha512-Q3obxKhBLVVbEQ8zsAmsQVobAAZhi8dFFFjF0q5xKXiaHvH8IkSxcbM27e46M9feUMieR03SPpmp5CtaNzpdBg== - dependencies: - "@types/node" "*" - "@types/js-beautify@*": version "1.8.0" resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.8.0.tgz#0369d3d0e1f35a6aec07cb4da2ee2bcda111367c" @@ -2559,26 +2547,19 @@ semver-compare@^1.0.0: integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= semver@^5.1.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.5, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@^7.3.2, semver@^7.3.5, semver@^7.3.8: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" diff --git a/cglicenses.json b/cglicenses.json index 585a819df1e..121cbc30c50 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -530,5 +530,11 @@ "IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] + }, + { + "name": "cacheable-request", + "prependLicenseText": [ + "Copyright (c) cacheable-request authors" + ] } ] diff --git a/cgmanifest.json b/cgmanifest.json index d5669cdf185..9d067989dbb 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "4ade2a6fb65e4b723feb7c09a5df765e5006b378" + "commitHash": "047fdbf3ca1958e4e489f0e777bf4022540f5ec2" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "22.3.14" + "version": "22.3.18" }, { "component": { diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 67bdd9b6c07..8e6e477991f 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -489,9 +489,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -1472,9 +1472,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.48" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if", @@ -1504,11 +1504,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt new file mode 100644 index 00000000000..ba85a27096c --- /dev/null +++ b/cli/ThirdPartyNotices.txt @@ -0,0 +1,10946 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + + + +--------------------------------------------------------- + +adler 1.0.2 - 0BSD OR MIT OR Apache-2.0 +https://github.com/jonas-schievink/adler + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +aho-corasick 1.0.1 - Unlicense OR MIT +https://github.com/BurntSushi/aho-corasick + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +android-tzdata 0.1.1 - MIT OR Apache-2.0 +https://github.com/RumovZ/android-tzdata + +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +android_system_properties 0.1.5 - MIT/Apache-2.0 +https://github.com/nical/android_system_properties + +The MIT License (MIT) + +Copyright (c) 2013 Nicolas Silva + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +anstream 0.3.2 - MIT OR Apache-2.0 +https://github.com/rust-cli/anstyle + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +anstyle 1.0.0 - MIT OR Apache-2.0 +https://github.com/rust-cli/anstyle + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +anstyle-parse 0.2.0 - MIT OR Apache-2.0 +https://github.com/rust-cli/anstyle + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +anstyle-query 1.0.0 - MIT OR Apache-2.0 +https://github.com/rust-cli/anstyle + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +anstyle-wincon 1.0.1 - MIT OR Apache-2.0 +https://github.com/rust-cli/anstyle + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-broadcast 0.5.1 - MIT OR Apache-2.0 +https://github.com/smol-rs/async-broadcast + +The MIT License (MIT) + +Copyright (c) 2020 Yoshua Wuyts + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-channel 1.8.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/async-channel + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-io 1.9.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/async-io + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-lock 2.7.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/async-lock + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-process 1.7.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/async-process + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-recursion 1.0.0 - MIT OR Apache-2.0 +https://github.com/dcchut/async-recursion + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-task 4.4.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/async-task + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +async-trait 0.1.68 - MIT OR Apache-2.0 +https://github.com/dtolnay/async-trait + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +atomic-waker 1.1.1 - Apache-2.0 OR MIT +https://github.com/smol-rs/atomic-waker + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +atty 0.2.14 - MIT +https://github.com/softprops/atty + +The MIT License (MIT) + +Copyright (c) 2015-2019 Doug Tangren + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +autocfg 1.1.0 - Apache-2.0 OR MIT +https://github.com/cuviper/autocfg + +Copyright (c) 2018 Josh Stone + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +base64 0.13.0 - MIT/Apache-2.0 +base64 0.21.2 - MIT OR Apache-2.0 +https://github.com/marshallpierce/rust-base64 + +The MIT License (MIT) + +Copyright (c) 2015 Alice Maz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +bit-vec 0.6.3 - MIT/Apache-2.0 +https://github.com/contain-rs/bit-vec + +Copyright (c) 2023 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +bitflags 1.3.2 - MIT/Apache-2.0 +https://github.com/bitflags/bitflags + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +block-buffer 0.10.3 - MIT OR Apache-2.0 +https://github.com/RustCrypto/utils + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block-padding +[`cmov`]: ./cmov +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`inout`]: ./inout +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof +--------------------------------------------------------- + +--------------------------------------------------------- + +block-padding 0.3.2 - MIT OR Apache-2.0 +https://github.com/RustCrypto/utils + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block-padding +[`cmov`]: ./cmov +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`inout`]: ./inout +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof +--------------------------------------------------------- + +--------------------------------------------------------- + +blocking 1.3.1 - Apache-2.0 OR MIT +https://github.com/smol-rs/blocking + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +bumpalo 3.12.0 - MIT/Apache-2.0 +https://github.com/fitzgen/bumpalo + +Copyright (c) 2019 Nick Fitzgerald + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +byteorder 1.4.3 - Unlicense OR MIT +https://github.com/BurntSushi/byteorder + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +bytes 1.4.0 - MIT +https://github.com/tokio-rs/bytes + +The MIT License (MIT) + +Copyright (c) 2018 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cache-padded 1.2.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/cache-padded + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cc 1.0.73 - MIT/Apache-2.0 +https://github.com/rust-lang/cc-rs + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cfg-if 1.0.0 - MIT/Apache-2.0 +https://github.com/alexcrichton/cfg-if + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +chrono 0.4.26 - MIT/Apache-2.0 +https://github.com/chronotope/chrono + +Rust-chrono is dual-licensed under The MIT License [1] and +Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and +contributors. + +Nota Bene: This is same as the Rust Project's own license. + + +[1]: , which is reproduced below: + +~~~~ +The MIT License (MIT) + +Copyright (c) 2014, Kang Seonghoon. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +~~~~ + + +[2]: , which is reproduced below: + +~~~~ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +~~~~ +--------------------------------------------------------- + +--------------------------------------------------------- + +clap 4.3.0 - MIT OR Apache-2.0 +https://github.com/clap-rs/clap + +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +clap_builder 4.3.0 - MIT OR Apache-2.0 +https://github.com/clap-rs/clap + +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +clap_derive 4.3.0 - MIT OR Apache-2.0 +https://github.com/clap-rs/clap/tree/master/clap_derive + +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +clap_lex 0.5.0 - MIT OR Apache-2.0 +https://github.com/clap-rs/clap/tree/master/clap_lex + +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +codespan-reporting 0.11.1 - Apache-2.0 +https://github.com/brendanzab/codespan + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +colorchoice 1.0.0 - MIT OR Apache-2.0 +https://github.com/rust-cli/anstyle + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +concurrent-queue 1.2.4 - Apache-2.0 OR MIT +concurrent-queue 2.2.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/concurrent-queue + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +console 0.15.7 - MIT +https://github.com/console-rs/console + +The MIT License (MIT) + +Copyright (c) 2017 Armin Ronacher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +const_format 0.2.31 - Zlib +https://github.com/rodrimati1992/const_format_crates/ + +Copyright (c) 2020 Matias Rodriguez. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +--------------------------------------------------------- + +--------------------------------------------------------- + +const_format_proc_macros 0.2.31 - Zlib +https://github.com/rodrimati1992/const_format_crates/ + +Copyright (c) 2020 Matias Rodriguez. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +--------------------------------------------------------- + +--------------------------------------------------------- + +core-foundation 0.9.3 - MIT / Apache-2.0 +https://github.com/servo/core-foundation-rs + +Copyright (c) 2012-2013 Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +core-foundation-sys 0.8.3 - MIT / Apache-2.0 +https://github.com/servo/core-foundation-rs + +Copyright (c) 2012-2013 Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cpufeatures 0.2.8 - MIT OR Apache-2.0 +https://github.com/RustCrypto/utils + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block-padding +[`cmov`]: ./cmov +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`inout`]: ./inout +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof +--------------------------------------------------------- + +--------------------------------------------------------- + +crc32fast 1.3.2 - MIT OR Apache-2.0 +https://github.com/srijs/rust-crc32fast + +MIT License + +Copyright (c) 2018 Sam Rijs, Alex Crichton and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +crossbeam-channel 0.5.6 - MIT OR Apache-2.0 +https://github.com/crossbeam-rs/crossbeam + +The MIT License (MIT) + +Copyright (c) 2019 The Crossbeam Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +crossbeam-utils 0.8.12 - MIT OR Apache-2.0 +https://github.com/crossbeam-rs/crossbeam + +The MIT License (MIT) + +Copyright (c) 2019 The Crossbeam Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +crypto-common 0.1.6 - MIT OR Apache-2.0 +https://github.com/RustCrypto/traits + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260050-traits +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/traits/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/traits +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg +[msrv-1.65]: https://img.shields.io/badge/rustc-1.65.0+-blue.svg + +[//]: # (crates) + +[`aead`]: ./aead +[`async‑signature`]: ./signature/async +[`cipher`]: ./cipher +[`crypto‑common`]: ./crypto-common +[`crypto`]: ./crypto +[`digest`]: ./digest +[`elliptic‑curve`]: ./elliptic-curve +[`kem`]: ./kem +[`password-hash`]: ./password-hash +[`signature`]: ./signature +[`universal‑hash`]: ./universal-hash + +[//]: # (algorithms) + +[Authenticated encryption]: https://en.wikipedia.org/wiki/Authenticated_encryption +[Block]: https://en.wikipedia.org/wiki/Block_cipher +[Message authentication code]: https://en.wikipedia.org/wiki/Message_authentication_code +[Cryptographic hash function]: https://en.wikipedia.org/wiki/Cryptographic_hash_function +[Digital signature]: https://en.wikipedia.org/wiki/Digital_signature +[Elliptic curve cryptography]: https://en.wikipedia.org/wiki/Elliptic-curve_cryptography +[Key encapsulation mechanism]: https://en.wikipedia.org/wiki/Key_encapsulation +[Password hashing]: https://en.wikipedia.org/wiki/Cryptographic_hash_function#Password_verification +[Stream cipher]: https://en.wikipedia.org/wiki/Stream_cipher +[Universal hash function]: https://en.wikipedia.org/wiki/Universal_hashing +--------------------------------------------------------- + +--------------------------------------------------------- + +cxx 1.0.78 - MIT OR Apache-2.0 +https://github.com/dtolnay/cxx + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cxx-build 1.0.78 - MIT OR Apache-2.0 +https://github.com/dtolnay/cxx + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cxxbridge-flags 1.0.78 - MIT OR Apache-2.0 +https://github.com/dtolnay/cxx + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +cxxbridge-macro 1.0.78 - MIT OR Apache-2.0 +https://github.com/dtolnay/cxx + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +data-encoding 2.3.2 - MIT +https://github.com/ia0/data-encoding + +The MIT License (MIT) + +Copyright (c) 2015-2020 Julien Cretin +Copyright (c) 2017-2020 Google Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +derivative 2.2.0 - MIT/Apache-2.0 +https://github.com/mcarton/rust-derivative + +Copyright (c) 2016 Martin Carton + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +dialoguer 0.10.4 - MIT +https://github.com/mitsuhiko/dialoguer + +The MIT License (MIT) + +Copyright (c) 2017 Armin Ronacher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +digest 0.10.5 - MIT OR Apache-2.0 +https://github.com/RustCrypto/traits + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260050-traits +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/traits/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/traits +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg +[msrv-1.65]: https://img.shields.io/badge/rustc-1.65.0+-blue.svg + +[//]: # (crates) + +[`aead`]: ./aead +[`async‑signature`]: ./signature/async +[`cipher`]: ./cipher +[`crypto‑common`]: ./crypto-common +[`crypto`]: ./crypto +[`digest`]: ./digest +[`elliptic‑curve`]: ./elliptic-curve +[`kem`]: ./kem +[`password-hash`]: ./password-hash +[`signature`]: ./signature +[`universal‑hash`]: ./universal-hash + +[//]: # (algorithms) + +[Authenticated encryption]: https://en.wikipedia.org/wiki/Authenticated_encryption +[Block]: https://en.wikipedia.org/wiki/Block_cipher +[Message authentication code]: https://en.wikipedia.org/wiki/Message_authentication_code +[Cryptographic hash function]: https://en.wikipedia.org/wiki/Cryptographic_hash_function +[Digital signature]: https://en.wikipedia.org/wiki/Digital_signature +[Elliptic curve cryptography]: https://en.wikipedia.org/wiki/Elliptic-curve_cryptography +[Key encapsulation mechanism]: https://en.wikipedia.org/wiki/Key_encapsulation +[Password hashing]: https://en.wikipedia.org/wiki/Cryptographic_hash_function#Password_verification +[Stream cipher]: https://en.wikipedia.org/wiki/Stream_cipher +[Universal hash function]: https://en.wikipedia.org/wiki/Universal_hashing +--------------------------------------------------------- + +--------------------------------------------------------- + +dirs 4.0.0 - MIT OR Apache-2.0 +dirs 5.0.1 - MIT OR Apache-2.0 +https://github.com/dirs-dev/dirs-rs + +Copyright (c) 2018-2019 dirs-rs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +dirs-sys 0.3.7 - MIT OR Apache-2.0 +dirs-sys 0.4.1 - MIT OR Apache-2.0 +https://github.com/dirs-dev/dirs-sys-rs + +Copyright (c) 2018-2019 dirs-rs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +encode_unicode 0.3.6 - MIT/Apache-2.0 +https://github.com/tormol/encode_unicode + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +encoding_rs 0.8.31 - (Apache-2.0 OR MIT) AND BSD-3-Clause +https://github.com/hsivonen/encoding_rs + +Copyright Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +enumflags2 0.7.7 - MIT OR Apache-2.0 +https://github.com/meithecatte/enumflags2 + +Copyright (c) 2017-2023 Maik Klein, Maja Kądziołka + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +enumflags2_derive 0.7.7 - MIT OR Apache-2.0 +https://github.com/meithecatte/enumflags2 + +Copyright (c) 2017-2023 Maik Klein, Maja Kądziołka + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +errno 0.3.1 - MIT OR Apache-2.0 +https://github.com/lambda-fairy/rust-errno + +Copyright (c) 2014 Chris Wong + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +errno-dragonfly 0.1.2 - MIT +https://github.com/mneumann/errno-dragonfly-rs + +MIT License + +Copyright (c) 2017 Michael Neumann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +event-listener 2.5.3 - Apache-2.0 OR MIT +https://github.com/smol-rs/event-listener + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +fastrand 1.8.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/fastrand + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +filetime 0.2.17 - MIT/Apache-2.0 +https://github.com/alexcrichton/filetime + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +flate2 1.0.26 - MIT OR Apache-2.0 +https://github.com/rust-lang/flate2-rs + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +fnv 1.0.7 - Apache-2.0 / MIT +https://github.com/servo/rust-fnv + +Copyright (c) 2017 Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +foreign-types 0.3.2 - MIT/Apache-2.0 +https://github.com/sfackler/foreign-types + +Copyright (c) 2017 The foreign-types Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +foreign-types-shared 0.1.1 - MIT/Apache-2.0 +https://github.com/sfackler/foreign-types + +Copyright (c) 2017 The foreign-types Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +form_urlencoded 1.1.0 - MIT OR Apache-2.0 +https://github.com/servo/rust-url + +Copyright (c) 2013-2022 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-channel 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-core 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-executor 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-io 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-lite 1.12.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/futures-lite + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-macro 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-sink 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-task 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +futures-util 0.3.28 - MIT OR Apache-2.0 +https://github.com/rust-lang/futures-rs + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +generic-array 0.14.6 - MIT +https://github.com/fizyk20/generic-array + +The MIT License (MIT) + +Copyright (c) 2015 Bartłomiej Kamiński + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +gethostname 0.4.3 - Apache-2.0 +https://github.com/swsnr/gethostname.rs + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +getrandom 0.1.16 - MIT OR Apache-2.0 +getrandom 0.2.7 - MIT OR Apache-2.0 +https://github.com/rust-random/getrandom + +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +h2 0.3.17 - MIT +https://github.com/hyperium/h2 + +The MIT License (MIT) + +Copyright (c) 2017 h2 authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +hashbrown 0.12.3 - MIT OR Apache-2.0 +https://github.com/rust-lang/hashbrown + +Copyright (c) 2016 Amanieu d'Antras + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +heck 0.4.0 - MIT OR Apache-2.0 +https://github.com/withoutboats/heck + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +hermit-abi 0.1.19 - MIT/Apache-2.0 +hermit-abi 0.3.1 - MIT OR Apache-2.0 +https://github.com/hermitcore/rusty-hermit + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +hex 0.4.3 - MIT OR Apache-2.0 +https://github.com/KokaKiwi/rust-hex + +Copyright (c) 2013-2014 The Rust Project Developers. +Copyright (c) 2015-2020 The rust-hex Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +hex-literal 0.3.4 - MIT OR Apache-2.0 +https://github.com/RustCrypto/utils + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block-padding +[`cmov`]: ./cmov +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`inout`]: ./inout +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof +--------------------------------------------------------- + +--------------------------------------------------------- + +hmac 0.12.1 - MIT OR Apache-2.0 +https://github.com/RustCrypto/MACs + +All crates licensed under either of + +* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260044-MACs +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/MACs/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/MACs +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg + +[//]: # (crates) + +[`belt-mac`]: ./belt-mac +[`cbc-mac`]: ./cbc-mac +[`cmac`]: ./cmac +[`hmac`]: ./hmac +[`pmac`]: ./pmac + +[//]: # (footnotes) + +[1]: https://en.wikipedia.org/wiki/Message_authentication_code + +[//]: # (algorithms) + +[BelT MAC]: https://apmi.bsu.by/assets/files/std/belt-spec371.pdf +[CBC-MAC]: https://en.wikipedia.org/wiki/CBC-MAC +[CMAC]: https://en.wikipedia.org/wiki/One-key_MAC +[HMAC]: https://en.wikipedia.org/wiki/HMAC +[PMAC]: https://en.wikipedia.org/wiki/PMAC_(cryptography) +--------------------------------------------------------- + +--------------------------------------------------------- + +http 0.2.8 - MIT OR Apache-2.0 +https://github.com/hyperium/http + +Copyright (c) 2017 http-rs authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +http-body 0.4.5 - MIT +https://github.com/hyperium/http-body + +The MIT License (MIT) + +Copyright (c) 2019 Hyper Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +httparse 1.8.0 - MIT/Apache-2.0 +https://github.com/seanmonstar/httparse + +Copyright (c) 2015-2021 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +httpdate 1.0.2 - MIT/Apache-2.0 +https://github.com/pyfisch/httpdate + +Copyright (c) 2016 Pyfisch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +hyper 0.14.26 - MIT +https://github.com/hyperium/hyper + +The MIT License (MIT) + +Copyright (c) 2014-2021 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +hyper-tls 0.5.0 - MIT/Apache-2.0 +https://github.com/hyperium/hyper-tls + +Copyright (c) 2017 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +iana-time-zone 0.1.51 - MIT OR Apache-2.0 +https://github.com/strawlab/iana-time-zone + +Copyright (c) 2020 Andrew D. Straw + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +iana-time-zone-haiku 0.1.0 - MIT OR Apache-2.0 +https://github.com/strawlab/iana-time-zone + +Copyright (c) 2020 Andrew D. Straw + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +idna 0.3.0 - MIT OR Apache-2.0 +https://github.com/servo/rust-url/ + +Copyright (c) 2013-2022 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +indexmap 1.9.1 - Apache-2.0 OR MIT +https://github.com/bluss/indexmap + +Copyright (c) 2016--2017 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +indicatif 0.17.4 - MIT +https://github.com/console-rs/indicatif + +The MIT License (MIT) + +Copyright (c) 2017 Armin Ronacher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +inout 0.1.3 - MIT OR Apache-2.0 +https://github.com/RustCrypto/utils + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block-padding +[`cmov`]: ./cmov +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`inout`]: ./inout +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof +--------------------------------------------------------- + +--------------------------------------------------------- + +instant 0.1.12 - BSD-3-Clause +https://github.com/sebcrozet/instant + +Copyright (c) 2019, SĆ©bastien Crozet +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------- + +--------------------------------------------------------- + +io-lifetimes 1.0.11 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +https://github.com/sunfishcode/io-lifetimes + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +ipnet 2.5.0 - MIT OR Apache-2.0 +https://github.com/krisprice/ipnet + +Copyright 2017 Juniper Networks, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +is-docker 0.2.0 - MIT +https://github.com/TheLarkInn/is-docker + +MIT License + +Copyright (c) 2023 Sean Larkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +is-terminal 0.4.7 - MIT +https://github.com/sunfishcode/is-terminal + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +is-wsl 0.4.0 - MIT +https://github.com/TheLarkInn/is-wsl + +MIT License + +Copyright (c) 2023 Sean Larkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +itoa 1.0.4 - MIT OR Apache-2.0 +https://github.com/dtolnay/itoa + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +js-sys 0.3.60 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +keyring 2.0.3 - MIT OR Apache-2.0 +https://github.com/hwchen/keyring-rs + +Copyright (c) 2016 keyring Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +lazy_static 1.4.0 - MIT/Apache-2.0 +https://github.com/rust-lang-nursery/lazy-static.rs + +Copyright (c) 2010 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +libc 0.2.144 - MIT OR Apache-2.0 +https://github.com/rust-lang/libc + +Copyright (c) 2014-2020 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +link-cplusplus 1.0.7 - MIT OR Apache-2.0 +https://github.com/dtolnay/link-cplusplus + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +linux-keyutils 0.2.3 - Apache-2.0 OR MIT +https://github.com/landhb/linux-keyutils + +Licensed under either of the following at your discretion: + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you shall be dual licensed as above, without any +additional terms or conditions. + +[//]: # (badges) +[license-badge]: https://img.shields.io/badge/license-MIT/Apache--2.0-lightgray.svg?style=flat-square +[license]: #license +[rust-version-badge]: https://img.shields.io/badge/rust-latest%20stable-blue.svg?style=flat-square +[rust-version]: #rust-version-policy +[cargo-badge-lib]: https://img.shields.io/crates/v/linux-keyutils.svg?style=flat-square&label=linux-keyutils +[cargo-lib]: https://crates.io/crates/linux-keyutils +[docs-badge-lib]: https://img.shields.io/docsrs/linux-keyutils/latest?style=flat-square +[docs-lib]: https://docs.rs/linux-keyutils +[codecov]: https://img.shields.io/codecov/c/github/landhb/linux-keyutils?style=flat-square +[codecov-url]: https://codecov.io/gh/landhb/linux-keyutils +[build]: https://img.shields.io/github/actions/workflow/status/landhb/linux-keyutils/checks.yml?branch=main&style=flat-square +[build-url]: https://github.com/landhb/linux-keyutils/actions?query=workflow%3Achecks +--------------------------------------------------------- + +--------------------------------------------------------- + +linux-raw-sys 0.3.8 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +https://github.com/sunfishcode/linux-raw-sys + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +lock_api 0.4.9 - MIT OR Apache-2.0 +https://github.com/Amanieu/parking_lot + +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +log 0.4.18 - MIT OR Apache-2.0 +https://github.com/rust-lang/log + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +md5 0.7.0 - Apache-2.0/MIT +https://github.com/stainless-steel/md5 + +The project is dual licensed under the terms of the Apache License, Version 2.0, +and the MIT License. You may obtain copies of the two licenses at + +* https://www.apache.org/licenses/LICENSE-2.0 and +* https://opensource.org/licenses/MIT, respectively. + +The following two notices apply to every file of the project. + +## The Apache License + +``` +Copyright 2015–2019 The md5 Developers + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +``` + +## The MIT License + +``` +Copyright 2015–2019 The md5 Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` +--------------------------------------------------------- + +--------------------------------------------------------- + +memchr 2.5.0 - Unlicense/MIT +https://github.com/BurntSushi/memchr + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +memoffset 0.7.1 - MIT +https://github.com/Gilnaa/memoffset + +The MIT License (MIT) + +Copyright (c) 2017 Gilad Naaman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +mime 0.3.16 - MIT/Apache-2.0 +https://github.com/hyperium/mime + +Copyright (c) 2014-2019 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +miniz_oxide 0.7.1 - MIT OR Zlib OR Apache-2.0 +https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide + +This library (excluding the miniz C code used for tests) is licensed under the MIT license. The library is based on the miniz C library, of which the parts used are dual-licensed under the [MIT license](https://github.com/Frommi/miniz_oxide/blob/master/miniz/miniz.c#L1) and also the [unlicense](https://github.com/Frommi/miniz_oxide/blob/master/miniz/miniz.c#L577). +The parts of miniz that are not covered by the unlicense is [some Zip64 code](https://github.com/richgel999/miniz/commit/224d207ce8fffb908e156d27478be3afb5d83e6a#diff-edc0e9ccfae3b5324b85b3ec0a53dc74) which is only MIT licensed. This and other Zip functionality in miniz is not part of the miniz_oxidde and miniz_oxide_c_api rust libraries. +--------------------------------------------------------- + +--------------------------------------------------------- + +mio 0.8.4 - MIT +https://github.com/tokio-rs/mio + +The MIT License (MIT) + +Copyright (c) 2014 Carl Lerche and other MIO contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +native-tls 0.2.10 - MIT/Apache-2.0 +https://github.com/sfackler/rust-native-tls + +Copyright (c) 2016 The rust-native-tls Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +nix 0.26.2 - MIT +https://github.com/nix-rust/nix + +The MIT License (MIT) + +Copyright (c) 2015 Carl Lerche + nix-rust Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +ntapi 0.4.0 - Apache-2.0 OR MIT +https://github.com/MSxDOS/ntapi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"),to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num 0.4.0 - MIT OR Apache-2.0 +https://github.com/rust-num/num + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num-bigint 0.4.3 - MIT OR Apache-2.0 +https://github.com/rust-num/num-bigint + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num-complex 0.4.2 - MIT OR Apache-2.0 +https://github.com/rust-num/num-complex + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num-integer 0.1.45 - MIT OR Apache-2.0 +https://github.com/rust-num/num-integer + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num-iter 0.1.43 - MIT OR Apache-2.0 +https://github.com/rust-num/num-iter + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num-rational 0.4.1 - MIT OR Apache-2.0 +https://github.com/rust-num/num-rational + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num-traits 0.2.15 - MIT OR Apache-2.0 +https://github.com/rust-num/num-traits + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +num_cpus 1.13.1 - MIT OR Apache-2.0 +https://github.com/seanmonstar/num_cpus + +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +number_prefix 0.4.0 - MIT +https://github.com/ogham/rust-number-prefix + +MIT License + +Copyright (c) 2018 Benjamin Sago + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +once_cell 1.17.2 - MIT OR Apache-2.0 +https://github.com/matklad/once_cell + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +open 4.1.0 - MIT +https://github.com/Byron/open-rs + +The MIT License (MIT) + +Copyright Ā© `2015` `Sebastian Thiel` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +openssl 0.10.55 - Apache-2.0 +https://github.com/sfackler/rust-openssl + +Copyright 2011-2017 Google Inc. + 2013 Jack Lloyd + 2013-2014 Steven Fackler + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +openssl-macros 0.1.0 - MIT/Apache-2.0 + + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +openssl-probe 0.1.5 - MIT/Apache-2.0 +https://github.com/alexcrichton/openssl-probe + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +openssl-sys 0.9.90 - MIT +https://github.com/sfackler/rust-openssl + +The MIT License (MIT) + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +opentelemetry 0.19.0 - Apache-2.0 +https://github.com/open-telemetry/opentelemetry-rust + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +opentelemetry_api 0.19.0 - Apache-2.0 +https://github.com/open-telemetry/opentelemetry-rust + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +opentelemetry_sdk 0.19.0 - Apache-2.0 +https://github.com/open-telemetry/opentelemetry-rust + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +option-ext 0.2.0 - MPL-2.0 +https://github.com/soc/option-ext + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. +--------------------------------------------------------- + +--------------------------------------------------------- + +ordered-stream 0.2.0 - MIT OR Apache-2.0 +https://github.com/danieldg/ordered-stream + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +parking 2.0.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/parking + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +parking_lot 0.12.1 - MIT OR Apache-2.0 +https://github.com/Amanieu/parking_lot + +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +parking_lot_core 0.9.3 - MIT OR Apache-2.0 +https://github.com/Amanieu/parking_lot + +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +paste 1.0.9 - MIT OR Apache-2.0 +https://github.com/dtolnay/paste + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +pathdiff 0.2.1 - MIT/Apache-2.0 +https://github.com/Manishearth/pathdiff + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +percent-encoding 2.2.0 - MIT OR Apache-2.0 +https://github.com/servo/rust-url/ + +Copyright (c) 2013-2022 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +pin-project 1.1.0 - Apache-2.0 OR MIT +https://github.com/taiki-e/pin-project + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +pin-project-internal 1.1.0 - Apache-2.0 OR MIT +https://github.com/taiki-e/pin-project + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +pin-project-lite 0.2.9 - Apache-2.0 OR MIT +https://github.com/taiki-e/pin-project-lite + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +pin-utils 0.1.0 - MIT OR Apache-2.0 +https://github.com/rust-lang/pin-utils + +Copyright (c) 2018 The pin-utils authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +pkg-config 0.3.25 - MIT OR Apache-2.0 +https://github.com/rust-lang/pkg-config-rs + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +polling 2.3.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/polling + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +portable-atomic 1.3.3 - Apache-2.0 OR MIT +https://github.com/taiki-e/portable-atomic + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +ppv-lite86 0.2.16 - MIT/Apache-2.0 +https://github.com/cryptocorrosion/cryptocorrosion + +Copyright (c) 2019 The CryptoCorrosion Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +proc-macro-crate 1.2.1 - Apache-2.0/MIT +https://github.com/bkchr/proc-macro-crate + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +proc-macro2 1.0.59 - MIT OR Apache-2.0 +https://github.com/dtolnay/proc-macro2 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +quote 1.0.28 - MIT OR Apache-2.0 +https://github.com/dtolnay/quote + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +rand 0.7.3 - MIT OR Apache-2.0 +rand 0.8.5 - MIT OR Apache-2.0 +https://github.com/rust-random/rand + +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +rand_chacha 0.2.2 - MIT OR Apache-2.0 +rand_chacha 0.3.1 - MIT OR Apache-2.0 +https://github.com/rust-random/rand + +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +rand_core 0.5.1 - MIT OR Apache-2.0 +rand_core 0.6.4 - MIT OR Apache-2.0 +https://github.com/rust-random/rand + +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +rand_hc 0.2.0 - MIT/Apache-2.0 +https://github.com/rust-random/rngs + +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +redox_syscall 0.2.16 - MIT +redox_syscall 0.3.5 - MIT +https://github.com/redox-os/syscall + +Copyright (c) 2017 Redox OS Developers + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +redox_users 0.4.3 - MIT +https://gitlab.redox-os.org/redox-os/users + +The MIT License (MIT) + +Copyright (c) 2017 Jose Narvaez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +regex 1.8.3 - MIT OR Apache-2.0 +https://github.com/rust-lang/regex + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +regex-syntax 0.7.2 - MIT OR Apache-2.0 +https://github.com/rust-lang/regex/tree/master/regex-syntax + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +reqwest 0.11.18 - MIT OR Apache-2.0 +https://github.com/seanmonstar/reqwest + +Copyright (c) 2016 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +rmp 0.8.11 - MIT +https://github.com/3Hren/msgpack-rust + +MIT License + +Copyright (c) 2017 Evgeny Safronov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +rmp-serde 1.1.1 - MIT +https://github.com/3Hren/msgpack-rust + +MIT License + +Copyright (c) 2017 Evgeny Safronov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +russh 6a15199c784c0b6d171a6fec09ed730a5cd1350d +https://github.com/microsoft/vscode-russh + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +russh-cryptovec 6a15199c784c0b6d171a6fec09ed730a5cd1350d +https://github.com/microsoft/vscode-russh + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +russh-keys 6a15199c784c0b6d171a6fec09ed730a5cd1350d +https://github.com/microsoft/vscode-russh + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +rustix 0.37.19 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +https://github.com/bytecodealliance/rustix + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +ryu 1.0.11 - Apache-2.0 OR BSL-1.0 +https://github.com/dtolnay/ryu + + +Licensed under either of Apache License, Version +2.0 (LICENSE-APACHE) or Boost Software License 1.0 (LICENSE-BOOST) at your +option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + +--------------------------------------------------------- + +--------------------------------------------------------- + +schannel 0.1.20 - MIT +https://github.com/steffengy/schannel-rs + +The MIT License (MIT) + +Copyright (c) 2015 steffengy + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +scopeguard 1.1.0 - MIT/Apache-2.0 +https://github.com/bluss/scopeguard + +Copyright (c) 2016-2019 Ulrik Sverdrup "bluss" and scopeguard developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +scratch 1.0.2 - MIT OR Apache-2.0 +https://github.com/dtolnay/scratch + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +secret-service 3.0.1 - MIT OR Apache-2.0 +https://github.com/hwchen/secret-service-rs + +Copyright (c) 2016 secret-service Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +security-framework 2.7.0 - MIT OR Apache-2.0 +https://github.com/kornelski/rust-security-framework + +The MIT License (MIT) + +Copyright (c) 2015 Steven Fackler + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +security-framework-sys 2.6.1 - MIT OR Apache-2.0 +https://github.com/kornelski/rust-security-framework + +The MIT License (MIT) + +Copyright (c) 2015 Steven Fackler + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +serde 1.0.163 - MIT OR Apache-2.0 +https://github.com/serde-rs/serde + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +serde_bytes 0.11.9 - MIT OR Apache-2.0 +https://github.com/serde-rs/bytes + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +serde_derive 1.0.163 - MIT OR Apache-2.0 +https://github.com/serde-rs/serde + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +serde_json 1.0.96 - MIT OR Apache-2.0 +https://github.com/serde-rs/json + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +serde_repr 0.1.9 - MIT OR Apache-2.0 +https://github.com/dtolnay/serde-repr + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +serde_urlencoded 0.7.1 - MIT/Apache-2.0 +https://github.com/nox/serde_urlencoded + +Copyright (c) 2016 Anthony Ramine + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +sha-1 0.10.0 - MIT OR Apache-2.0 +https://github.com/RustCrypto/hashes + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/hashes +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg + +[//]: # (crates) + +[`ascon‑hash`]: ./ascon-hash +[`belt‑hash`]: ./belt-hash +[`blake2`]: ./blake2 +[`fsb`]: ./fsb +[`gost94`]: ./gost94 +[`groestl`]: ./groestl +[`jh`]: ./jh +[`k12`]: ./k12 +[`md2`]: ./md2 +[`md4`]: ./md4 +[`md5`]: ./md5 +[`ripemd`]: ./ripemd +[`sha1`]: ./sha1 +[`sha2`]: ./sha2 +[`sha3`]: ./sha3 +[`shabal`]: ./shabal +[`skein`]: ./skein +[`sm3`]: ./sm3 +[`streebog`]: ./streebog +[`tiger`]: ./tiger +[`whirlpool`]: ./whirlpool + +[//]: # (footnotes) + +[1]: https://en.wikipedia.org/wiki/Cryptographic_hash_function +[`blake3`]: https://github.com/BLAKE3-team/BLAKE3 +[`base16ct`]: https://docs.rs/base16ct +[`base64ct`]: https://docs.rs/base64ct +[`digest`]: https://docs.rs/digest +[`Digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html +[`Digest::digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html#tymethod.digest +[`DynDigest`]: https://docs.rs/digest/0.10.0/digest/trait.DynDigest.html +[`generic-array`]: https://docs.rs/generic-array +[HMAC]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code +[`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +[`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +[`hmac`]: https://docs.rs/hmac +[RustCrypto/MACs]: https://github.com/RustCrypto/MACs + +[//]: # (algorithms) + +[Ascon]: https://ascon.iaik.tugraz.at +[BelT]: https://ru.wikipedia.org/wiki/BelT +[BLAKE2]: https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2 +[FSB]: https://en.wikipedia.org/wiki/Fast_syndrome-based_hash +[GOST94]: https://en.wikipedia.org/wiki/GOST_(hash_function) +[GrĆøstl]: https://en.wikipedia.org/wiki/GrĆøstl +[JH]: https://www3.ntu.edu.sg/home/wuhj/research/jh +[KangarooTwelve]: https://keccak.team/kangarootwelve.html +[MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography) +[MD4]: https://en.wikipedia.org/wiki/MD4 +[MD5]: https://en.wikipedia.org/wiki/MD5 +[RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD +[SHA-1]: https://en.wikipedia.org/wiki/SHA-1 +[SHA-2]: https://en.wikipedia.org/wiki/SHA-2 +[SHA-3]: https://en.wikipedia.org/wiki/SHA-3 +[SHABAL]: https://www.cs.rit.edu/~ark/20090927/Round2Candidates/Shabal.pdf +[Skein]: https://schneier.com/academic/skein +[SM3]: https://en.wikipedia.org/wiki/SM3_(hash_function) +[Streebog]: https://en.wikipedia.org/wiki/Streebog +[Whirlpool]: https://en.wikipedia.org/wiki/Whirlpool_(cryptography) +[Tiger]: http://www.cs.technion.ac.il/~biham/Reports/Tiger/tiger/tiger.html +--------------------------------------------------------- + +--------------------------------------------------------- + +sha1 0.10.5 - MIT OR Apache-2.0 +https://github.com/RustCrypto/hashes + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/hashes +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg + +[//]: # (crates) + +[`ascon‑hash`]: ./ascon-hash +[`belt‑hash`]: ./belt-hash +[`blake2`]: ./blake2 +[`fsb`]: ./fsb +[`gost94`]: ./gost94 +[`groestl`]: ./groestl +[`jh`]: ./jh +[`k12`]: ./k12 +[`md2`]: ./md2 +[`md4`]: ./md4 +[`md5`]: ./md5 +[`ripemd`]: ./ripemd +[`sha1`]: ./sha1 +[`sha2`]: ./sha2 +[`sha3`]: ./sha3 +[`shabal`]: ./shabal +[`skein`]: ./skein +[`sm3`]: ./sm3 +[`streebog`]: ./streebog +[`tiger`]: ./tiger +[`whirlpool`]: ./whirlpool + +[//]: # (footnotes) + +[1]: https://en.wikipedia.org/wiki/Cryptographic_hash_function +[`blake3`]: https://github.com/BLAKE3-team/BLAKE3 +[`base16ct`]: https://docs.rs/base16ct +[`base64ct`]: https://docs.rs/base64ct +[`digest`]: https://docs.rs/digest +[`Digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html +[`Digest::digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html#tymethod.digest +[`DynDigest`]: https://docs.rs/digest/0.10.0/digest/trait.DynDigest.html +[`generic-array`]: https://docs.rs/generic-array +[HMAC]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code +[`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +[`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +[`hmac`]: https://docs.rs/hmac +[RustCrypto/MACs]: https://github.com/RustCrypto/MACs + +[//]: # (algorithms) + +[Ascon]: https://ascon.iaik.tugraz.at +[BelT]: https://ru.wikipedia.org/wiki/BelT +[BLAKE2]: https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2 +[FSB]: https://en.wikipedia.org/wiki/Fast_syndrome-based_hash +[GOST94]: https://en.wikipedia.org/wiki/GOST_(hash_function) +[GrĆøstl]: https://en.wikipedia.org/wiki/GrĆøstl +[JH]: https://www3.ntu.edu.sg/home/wuhj/research/jh +[KangarooTwelve]: https://keccak.team/kangarootwelve.html +[MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography) +[MD4]: https://en.wikipedia.org/wiki/MD4 +[MD5]: https://en.wikipedia.org/wiki/MD5 +[RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD +[SHA-1]: https://en.wikipedia.org/wiki/SHA-1 +[SHA-2]: https://en.wikipedia.org/wiki/SHA-2 +[SHA-3]: https://en.wikipedia.org/wiki/SHA-3 +[SHABAL]: https://www.cs.rit.edu/~ark/20090927/Round2Candidates/Shabal.pdf +[Skein]: https://schneier.com/academic/skein +[SM3]: https://en.wikipedia.org/wiki/SM3_(hash_function) +[Streebog]: https://en.wikipedia.org/wiki/Streebog +[Whirlpool]: https://en.wikipedia.org/wiki/Whirlpool_(cryptography) +[Tiger]: http://www.cs.technion.ac.il/~biham/Reports/Tiger/tiger/tiger.html +--------------------------------------------------------- + +--------------------------------------------------------- + +sha2 0.10.6 - MIT OR Apache-2.0 +https://github.com/RustCrypto/hashes + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/hashes +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg + +[//]: # (crates) + +[`ascon‑hash`]: ./ascon-hash +[`belt‑hash`]: ./belt-hash +[`blake2`]: ./blake2 +[`fsb`]: ./fsb +[`gost94`]: ./gost94 +[`groestl`]: ./groestl +[`jh`]: ./jh +[`k12`]: ./k12 +[`md2`]: ./md2 +[`md4`]: ./md4 +[`md5`]: ./md5 +[`ripemd`]: ./ripemd +[`sha1`]: ./sha1 +[`sha2`]: ./sha2 +[`sha3`]: ./sha3 +[`shabal`]: ./shabal +[`skein`]: ./skein +[`sm3`]: ./sm3 +[`streebog`]: ./streebog +[`tiger`]: ./tiger +[`whirlpool`]: ./whirlpool + +[//]: # (footnotes) + +[1]: https://en.wikipedia.org/wiki/Cryptographic_hash_function +[`blake3`]: https://github.com/BLAKE3-team/BLAKE3 +[`base16ct`]: https://docs.rs/base16ct +[`base64ct`]: https://docs.rs/base64ct +[`digest`]: https://docs.rs/digest +[`Digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html +[`Digest::digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html#tymethod.digest +[`DynDigest`]: https://docs.rs/digest/0.10.0/digest/trait.DynDigest.html +[`generic-array`]: https://docs.rs/generic-array +[HMAC]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code +[`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +[`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +[`hmac`]: https://docs.rs/hmac +[RustCrypto/MACs]: https://github.com/RustCrypto/MACs + +[//]: # (algorithms) + +[Ascon]: https://ascon.iaik.tugraz.at +[BelT]: https://ru.wikipedia.org/wiki/BelT +[BLAKE2]: https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2 +[FSB]: https://en.wikipedia.org/wiki/Fast_syndrome-based_hash +[GOST94]: https://en.wikipedia.org/wiki/GOST_(hash_function) +[GrĆøstl]: https://en.wikipedia.org/wiki/GrĆøstl +[JH]: https://www3.ntu.edu.sg/home/wuhj/research/jh +[KangarooTwelve]: https://keccak.team/kangarootwelve.html +[MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography) +[MD4]: https://en.wikipedia.org/wiki/MD4 +[MD5]: https://en.wikipedia.org/wiki/MD5 +[RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD +[SHA-1]: https://en.wikipedia.org/wiki/SHA-1 +[SHA-2]: https://en.wikipedia.org/wiki/SHA-2 +[SHA-3]: https://en.wikipedia.org/wiki/SHA-3 +[SHABAL]: https://www.cs.rit.edu/~ark/20090927/Round2Candidates/Shabal.pdf +[Skein]: https://schneier.com/academic/skein +[SM3]: https://en.wikipedia.org/wiki/SM3_(hash_function) +[Streebog]: https://en.wikipedia.org/wiki/Streebog +[Whirlpool]: https://en.wikipedia.org/wiki/Whirlpool_(cryptography) +[Tiger]: http://www.cs.technion.ac.il/~biham/Reports/Tiger/tiger/tiger.html +--------------------------------------------------------- + +--------------------------------------------------------- + +shell-escape 0.1.5 - MIT/Apache-2.0 +https://github.com/sfackler/shell-escape + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +shell-words 1.1.0 - MIT/Apache-2.0 +https://github.com/tmiasko/shell-words + +Copyright (c) 2016 Tomasz Miąsko + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +signal-hook 0.3.15 - Apache-2.0/MIT +https://github.com/vorner/signal-hook + +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +signal-hook-registry 1.4.0 - Apache-2.0/MIT +https://github.com/vorner/signal-hook + +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +slab 0.4.7 - MIT +https://github.com/tokio-rs/slab + +The MIT License (MIT) + +Copyright (c) 2019 Carl Lerche + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +smallvec 1.10.0 - MIT OR Apache-2.0 +https://github.com/servo/rust-smallvec + +Copyright (c) 2018 The Servo Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +socket2 0.4.9 - MIT OR Apache-2.0 +https://github.com/rust-lang/socket2 + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +static_assertions 1.1.0 - MIT OR Apache-2.0 +https://github.com/nvzqz/static-assertions-rs + +MIT License + +Copyright (c) 2017 Nikolai Vazquez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +strsim 0.10.0 - MIT +https://github.com/dguo/strsim-rs + +The MIT License (MIT) + +Copyright (c) 2015 Danny Guo +Copyright (c) 2016 Titus Wormer +Copyright (c) 2018 Akash Kurdekar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +subtle 2.4.1 - BSD-3-Clause +https://github.com/dalek-cryptography/subtle + +Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------- + +--------------------------------------------------------- + +syn 1.0.103 - MIT OR Apache-2.0 +syn 2.0.18 - MIT OR Apache-2.0 +https://github.com/dtolnay/syn + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +sysinfo 0.29.0 - MIT +https://github.com/GuillaumeGomez/sysinfo + +The MIT License (MIT) + +Copyright (c) 2015 Guillaume Gomez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tar 0.4.38 - MIT/Apache-2.0 +https://github.com/alexcrichton/tar-rs + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tempfile 3.5.0 - MIT OR Apache-2.0 +https://github.com/Stebalien/tempfile + +Copyright (c) 2015 Steven Allen + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +termcolor 1.1.3 - Unlicense OR MIT +https://github.com/BurntSushi/termcolor + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +thiserror 1.0.40 - MIT OR Apache-2.0 +https://github.com/dtolnay/thiserror + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +thiserror-impl 1.0.40 - MIT OR Apache-2.0 +https://github.com/dtolnay/thiserror + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +time 0.1.44 - MIT/Apache-2.0 +time 0.3.21 - MIT OR Apache-2.0 +https://github.com/time-rs/time + +Copyright (c) 2022 Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +time-core 0.1.1 - MIT OR Apache-2.0 +https://github.com/time-rs/time + +Copyright (c) 2022 Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tinyvec 1.6.0 - Zlib OR Apache-2.0 OR MIT +https://github.com/Lokathor/tinyvec + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tinyvec_macros 0.1.0 - MIT OR Apache-2.0 OR Zlib +https://github.com/Soveu/tinyvec_macros + +MIT License + +Copyright (c) 2020 Soveu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tokio 1.28.2 - MIT +https://github.com/tokio-rs/tokio + +The MIT License (MIT) + +Copyright (c) 2023 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tokio-macros 2.1.0 - MIT +https://github.com/tokio-rs/tokio + +The MIT License (MIT) + +Copyright (c) 2023 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tokio-native-tls 0.3.0 - MIT +https://github.com/tokio-rs/tls + +The MIT License (MIT) + +Copyright (c) 2019 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tokio-stream 0.1.11 - MIT +https://github.com/tokio-rs/tokio + +The MIT License (MIT) + +Copyright (c) 2023 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tokio-tungstenite 0.17.2 - MIT +https://github.com/snapview/tokio-tungstenite + +The MIT License (MIT) + +Copyright (c) 2017 Daniel Abramov +Copyright (c) 2017 Alexey Galakhov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tokio-util 0.7.8 - MIT +https://github.com/tokio-rs/tokio + +The MIT License (MIT) + +Copyright (c) 2023 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +toml 0.5.9 - MIT/Apache-2.0 +https://github.com/toml-rs/toml + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tower-service 0.3.2 - MIT +https://github.com/tower-rs/tower + +The MIT License (MIT) + +Copyright (c) 2019 Tower Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tracing 0.1.37 - MIT +https://github.com/tokio-rs/tracing + +The MIT License (MIT) + +Copyright (c) 2019 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tracing-attributes 0.1.23 - MIT +https://github.com/tokio-rs/tracing + +The MIT License (MIT) + +Copyright (c) 2019 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tracing-core 0.1.30 - MIT +https://github.com/tokio-rs/tracing + +The MIT License (MIT) + +Copyright (c) 2019 Tokio Contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +try-lock 0.2.3 - MIT +https://github.com/seanmonstar/try-lock + +The MIT License (MIT) + +Copyright (c) 2018 Sean McArthur +Copyright (c) 2016 Alex Crichton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tungstenite 0.17.3 - MIT/Apache-2.0 +https://github.com/snapview/tungstenite-rs + +Copyright (c) 2017 Alexey Galakhov +Copyright (c) 2016 Jason Housley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +tunnels 2621784a9ad72aa39500372391332a14bad581a3 +https://github.com/microsoft/dev-tunnels + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +typenum 1.15.0 - MIT OR Apache-2.0 +https://github.com/paholg/typenum + +MIT OR Apache-2.0 +--------------------------------------------------------- + +--------------------------------------------------------- + +uds_windows 1.0.2 - MIT +https://github.com/haraldh/rust_uds_windows + +MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +unicode-bidi 0.3.8 - MIT OR Apache-2.0 +https://github.com/servo/unicode-bidi + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +unicode-ident 1.0.5 - (MIT OR Apache-2.0) AND Unicode-DFS-2016 +https://github.com/dtolnay/unicode-ident + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +unicode-normalization 0.1.22 - MIT/Apache-2.0 +https://github.com/unicode-rs/unicode-normalization + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +unicode-width 0.1.10 - MIT/Apache-2.0 +https://github.com/unicode-rs/unicode-width + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +unicode-xid 0.2.4 - MIT OR Apache-2.0 +https://github.com/unicode-rs/unicode-xid + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +url 2.3.1 - MIT OR Apache-2.0 +https://github.com/servo/rust-url + +Copyright (c) 2013-2022 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +urlencoding 2.1.2 - MIT +https://github.com/kornelski/rust_urlencoding + +The MIT License (MIT) + +Ā© 2016 Bertram Truong +Ā© 2021 Kornel Lesiński + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +utf-8 0.7.6 - MIT OR Apache-2.0 +https://github.com/SimonSapin/rust-utf8 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +utf8parse 0.2.1 - Apache-2.0 OR MIT +https://github.com/alacritty/vte + +Copyright (c) 2016 Joe Wilm + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +uuid 0.8.2 - Apache-2.0 OR MIT +uuid 1.3.3 - Apache-2.0 OR MIT +https://github.com/uuid-rs/uuid + +Copyright (c) 2014 The Rust Project Developers +Copyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlon + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +vcpkg 0.2.15 - MIT/Apache-2.0 +https://github.com/mcgoo/vcpkg-rs + +Copyright (c) 2017 Jim McGrath + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +version_check 0.9.4 - MIT/Apache-2.0 +https://github.com/SergioBenitez/version_check + +The MIT License (MIT) +Copyright (c) 2017-2018 Sergio Benitez + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +waker-fn 1.1.0 - Apache-2.0 OR MIT +https://github.com/smol-rs/waker-fn + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +want 0.3.0 - MIT +https://github.com/seanmonstar/want + +The MIT License (MIT) + +Copyright (c) 2018-2019 Sean McArthur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasi 0.10.0+wasi-snapshot-preview1 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +wasi 0.11.0+wasi-snapshot-preview1 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +wasi 0.9.0+wasi-snapshot-preview1 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT +https://github.com/bytecodealliance/wasi + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-bindgen 0.2.83 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-bindgen-backend 0.2.83 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-bindgen-futures 0.4.33 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-bindgen-macro 0.2.83 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-bindgen-macro-support 0.2.83 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-bindgen-shared 0.2.83 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wasm-streams 0.2.3 - MIT OR Apache-2.0 +https://github.com/MattiasBuelens/wasm-streams/ + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +web-sys 0.3.60 - MIT/Apache-2.0 +https://github.com/rustwasm/wasm-bindgen/tree/master/crates/web-sys + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +wepoll-ffi 0.1.2 - MIT OR Apache-2.0 OR BSD-2-Clause +https://github.com/aclysma/wepoll-ffi + +MIT License + +Copyright (c) 2019-2020 Philip Degarmo and other wepoll-ffi contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +winapi 0.3.9 - MIT/Apache-2.0 +https://github.com/retep998/winapi-rs + +Copyright (c) 2015-2018 The winapi-rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +winapi-i686-pc-windows-gnu 0.4.0 - MIT/Apache-2.0 +https://github.com/retep998/winapi-rs + +Copyright (c) 2015-2018 The winapi-rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +winapi-util 0.1.5 - Unlicense/MIT +https://github.com/BurntSushi/winapi-util + +The MIT License (MIT) + +Copyright (c) 2017 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +winapi-x86_64-pc-windows-gnu 0.4.0 - MIT/Apache-2.0 +https://github.com/retep998/winapi-rs + +Copyright (c) 2015-2018 The winapi-rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +windows-sys 0.36.1 - MIT OR Apache-2.0 +windows-sys 0.45.0 - MIT OR Apache-2.0 +windows-sys 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows-targets 0.42.2 - MIT OR Apache-2.0 +windows-targets 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_aarch64_gnullvm 0.42.2 - MIT OR Apache-2.0 +windows_aarch64_gnullvm 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_aarch64_msvc 0.36.1 - MIT OR Apache-2.0 +windows_aarch64_msvc 0.42.2 - MIT OR Apache-2.0 +windows_aarch64_msvc 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_i686_gnu 0.36.1 - MIT OR Apache-2.0 +windows_i686_gnu 0.42.2 - MIT OR Apache-2.0 +windows_i686_gnu 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_i686_msvc 0.36.1 - MIT OR Apache-2.0 +windows_i686_msvc 0.42.2 - MIT OR Apache-2.0 +windows_i686_msvc 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_x86_64_gnu 0.36.1 - MIT OR Apache-2.0 +windows_x86_64_gnu 0.42.2 - MIT OR Apache-2.0 +windows_x86_64_gnu 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_x86_64_gnullvm 0.42.2 - MIT OR Apache-2.0 +windows_x86_64_gnullvm 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +windows_x86_64_msvc 0.36.1 - MIT OR Apache-2.0 +windows_x86_64_msvc 0.42.2 - MIT OR Apache-2.0 +windows_x86_64_msvc 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + +winnow 0.4.1 - MIT +https://github.com/winnow-rs/winnow + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +winreg 0.10.1 - MIT +winreg 0.50.0 - MIT +https://github.com/gentoo90/winreg-rs + +The MIT License (MIT) + +Copyright (c) 2015 Igor Shaula + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +xattr 0.2.3 - MIT/Apache-2.0 +https://github.com/Stebalien/xattr + +Copyright (c) 2015 Steven Allen + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +xdg-home 1.0.0 - MIT +https://github.com/zeenix/xdg-home + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +yasna 0.5.2 - MIT OR Apache-2.0 +https://github.com/qnighy/yasna.rs + +Copyright (c) 2016 Masaki Hara + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zbus 3.13.1 - MIT +https://github.com/dbus2/zbus/ + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zbus_macros 3.13.1 - MIT +https://github.com/dbus2/zbus/ + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zbus_names 2.5.1 - MIT +https://github.com/dbus2/zbus/ + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zeroize 1.3.0 - Apache-2.0 OR MIT +https://github.com/RustCrypto/utils/tree/master/zeroize + +All crates licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg +[deps-link]: https://deps.rs/repo/github/RustCrypto/utils + +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg +[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg +[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg + +[//]: # (crates) + +[`blobby`]: ./blobby +[`block-buffer`]: ./block-buffer +[`block‑padding`]: ./block-padding +[`cmov`]: ./cmov +[`collectable`]: ./collectable +[`cpufeatures`]: ./cpufeatures +[`dbl`]: ./dbl +[`hex-literal`]: ./hex-literal +[`inout`]: ./inout +[`opaque-debug`]: ./opaque-debug +[`wycheproof2blb`]: ./wycheproof2blb +[`zeroize`]: ./zeroize + +[//]: # (misc) + +[Wycheproof]: https://github.com/google/wycheproof +--------------------------------------------------------- + +--------------------------------------------------------- + +zip 0.6.6 - MIT +https://github.com/zip-rs/zip + +The MIT License (MIT) + +Copyright (c) 2014 Mathijs van de Nes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zvariant 3.14.0 - MIT +https://github.com/dbus2/zbus/ + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zvariant_derive 3.14.0 - MIT +https://github.com/dbus2/zbus/ + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +zvariant_utils 1.0.1 - MIT +https://github.com/dbus2/zbus/ + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- \ No newline at end of file diff --git a/cli/build.rs b/cli/build.rs index bcf1bf27e0a..41e289774e9 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -25,7 +25,7 @@ fn apply_build_environment_variables() { } let pkg_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let mut cmd = Command::new("node"); + let mut cmd = Command::new(env::var("NODE_PATH").unwrap_or_else(|_| "node".to_string())); cmd.arg("../build/azure-pipelines/cli/prepare.js"); cmd.current_dir(&pkg_dir); cmd.env("VSCODE_CLI_PREPARE_OUTPUT", "json"); diff --git a/cli/src/async_pipe.rs b/cli/src/async_pipe.rs index dcbe0d16017..6c7c918967a 100644 --- a/cli/src/async_pipe.rs +++ b/cli/src/async_pipe.rs @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ use crate::{constants::APPLICATION_NAME, util::errors::CodeError}; +use async_trait::async_trait; use std::path::{Path, PathBuf}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::TcpListener; use uuid::Uuid; // todo: we could probably abstract this into some crate, if one doesn't already exist @@ -39,7 +42,7 @@ cfg_if::cfg_if! { pipe.into_split() } } else { - use tokio::{time::sleep, io::{AsyncRead, AsyncWrite, ReadBuf}}; + use tokio::{time::sleep, io::ReadBuf}; use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions, NamedPipeClient, NamedPipeServer}; use std::{time::Duration, pin::Pin, task::{Context, Poll}, io}; use pin_project::pin_project; @@ -181,3 +184,34 @@ pub fn get_socket_name() -> PathBuf { } } } + +pub type AcceptedRW = ( + Box, + Box, +); + +#[async_trait] +pub trait AsyncRWAccepter { + async fn accept_rw(&mut self) -> Result; +} + +#[async_trait] +impl AsyncRWAccepter for AsyncPipeListener { + async fn accept_rw(&mut self) -> Result { + let pipe = self.accept().await?; + let (read, write) = socket_stream_split(pipe); + Ok((Box::new(read), Box::new(write))) + } +} + +#[async_trait] +impl AsyncRWAccepter for TcpListener { + async fn accept_rw(&mut self) -> Result { + let (stream, _) = self + .accept() + .await + .map_err(CodeError::AsyncPipeListenerFailed)?; + let (read, write) = tokio::io::split(stream); + Ok((Box::new(read), Box::new(write))) + } +} diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 604b1a6ced7..42bd3a894dc 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -10,7 +10,8 @@ use crate::{ trace, util::{ errors::{ - wrap, AnyError, OAuthError, RefreshTokenNotAvailableError, StatusError, WrappedError, + wrap, AnyError, CodeError, OAuthError, RefreshTokenNotAvailableError, StatusError, + WrappedError, }, input::prompt_options, }, @@ -160,6 +161,7 @@ impl StoredCredential { struct StorageWithLastRead { storage: Box, + fallback_storage: Option, last_read: Cell, WrappedError>>, } @@ -172,9 +174,9 @@ pub struct Auth { } trait StorageImplementation: Send + Sync { - fn read(&mut self) -> Result, WrappedError>; - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError>; - fn clear(&mut self) -> Result<(), WrappedError>; + fn read(&mut self) -> Result, AnyError>; + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError>; + fn clear(&mut self) -> Result<(), AnyError>; } // unseal decrypts and deserializes the value @@ -217,16 +219,34 @@ struct ThreadKeyringStorage { } impl ThreadKeyringStorage { - fn thread_op(&mut self, f: Fn) -> R + fn thread_op(&mut self, f: Fn) -> Result where - Fn: 'static + Send + FnOnce(&mut KeyringStorage) -> R, + Fn: 'static + Send + FnOnce(&mut KeyringStorage) -> Result, R: 'static + Send, { - let mut s = self.s.take().unwrap(); - let handler = thread::spawn(move || (f(&mut s), s)); - let (r, s) = handler.join().unwrap(); - self.s = Some(s); - r + let mut s = match self.s.take() { + Some(s) => s, + None => return Err(CodeError::KeyringTimeout.into()), + }; + + // It seems like on Linux communication to the keyring can block indefinitely. + // Fall back after a 5 second timeout. + let (sender, receiver) = std::sync::mpsc::channel(); + let tsender = sender.clone(); + + thread::spawn(move || sender.send(Some((f(&mut s), s)))); + thread::spawn(move || { + thread::sleep(std::time::Duration::from_secs(5)); + let _ = tsender.send(None); + }); + + match receiver.recv().unwrap() { + Some((r, s)) => { + self.s = Some(s); + r + } + None => Err(CodeError::KeyringTimeout.into()), + } } } @@ -239,15 +259,15 @@ impl Default for ThreadKeyringStorage { } impl StorageImplementation for ThreadKeyringStorage { - fn read(&mut self) -> Result, WrappedError> { + fn read(&mut self) -> Result, AnyError> { self.thread_op(|s| s.read()) } - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError> { + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError> { self.thread_op(move |s| s.store(value)) } - fn clear(&mut self) -> Result<(), WrappedError> { + fn clear(&mut self) -> Result<(), AnyError> { self.thread_op(|s| s.clear()) } } @@ -273,7 +293,7 @@ macro_rules! get_next_entry { } impl StorageImplementation for KeyringStorage { - fn read(&mut self) -> Result, WrappedError> { + fn read(&mut self) -> Result, AnyError> { let mut str = String::new(); for i in 0.. { @@ -281,7 +301,7 @@ impl StorageImplementation for KeyringStorage { let next_chunk = match entry.get_password() { Ok(value) => value, Err(keyring::Error::NoEntry) => return Ok(None), // missing entries? - Err(e) => return Err(wrap(e, "error reading keyring")), + Err(e) => return Err(wrap(e, "error reading keyring").into()), }; if next_chunk.ends_with(CONTINUE_MARKER) { @@ -295,7 +315,7 @@ impl StorageImplementation for KeyringStorage { Ok(unseal(&str)) } - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError> { + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError> { let sealed = seal(&value); let step_size = KEYCHAIN_ENTRY_LIMIT - CONTINUE_MARKER.len(); @@ -312,14 +332,14 @@ impl StorageImplementation for KeyringStorage { }; if let Err(e) = stored { - return Err(wrap(e, "error updating keyring")); + return Err(wrap(e, "error updating keyring").into()); } } Ok(()) } - fn clear(&mut self) -> Result<(), WrappedError> { + fn clear(&mut self) -> Result<(), AnyError> { self.read().ok(); // make sure component parts are available for entry in self.entries.iter() { entry @@ -335,16 +355,16 @@ impl StorageImplementation for KeyringStorage { struct FileStorage(PersistedState>); impl StorageImplementation for FileStorage { - fn read(&mut self) -> Result, WrappedError> { + fn read(&mut self) -> Result, AnyError> { Ok(self.0.load().and_then(|s| unseal(&s))) } - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError> { - self.0.save(Some(seal(&value))) + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError> { + self.0.save(Some(seal(&value))).map_err(|e| e.into()) } - fn clear(&mut self) -> Result<(), WrappedError> { - self.0.save(None) + fn clear(&mut self) -> Result<(), AnyError> { + self.0.save(None).map_err(|e| e.into()) } } @@ -373,20 +393,32 @@ impl Auth { let mut keyring_storage = ThreadKeyringStorage::default(); let mut file_storage = FileStorage(PersistedState::new(self.file_storage_path.clone())); - let keyring_storage_result = match std::env::var("VSCODE_CLI_USE_FILE_KEYCHAIN") { - Ok(_) => Err(wrap("", "user prefers file storage")), - _ => keyring_storage.read(), + let native_storage_result = if std::env::var("VSCODE_CLI_USE_FILE_KEYCHAIN").is_ok() + || self.file_storage_path.exists() + { + Err(wrap("", "user prefers file storage").into()) + } else { + keyring_storage.read() }; - let mut storage = match keyring_storage_result { + let mut storage = match native_storage_result { Ok(v) => StorageWithLastRead { last_read: Cell::new(Ok(v)), + fallback_storage: Some(file_storage), storage: Box::new(keyring_storage), }, - Err(_) => StorageWithLastRead { - last_read: Cell::new(file_storage.read()), - storage: Box::new(file_storage), - }, + Err(e) => { + debug!(self.log, "Using file keychain storage due to: {}", e); + StorageWithLastRead { + last_read: Cell::new( + file_storage + .read() + .map_err(|e| wrap(e, "could not read from file storage")), + ), + fallback_storage: None, + storage: Box::new(file_storage), + } + } }; let out = op(&mut storage); @@ -419,7 +451,7 @@ impl Auth { } /// Clears login info from the keyring. - pub fn clear_credentials(&self) -> Result<(), WrappedError> { + pub fn clear_credentials(&self) -> Result<(), AnyError> { self.with_storage(|storage| { storage.storage.clear()?; storage.last_read.set(Ok(None)); @@ -505,7 +537,18 @@ impl Auth { "Failed to update keyring with new credentials: {}", e ); + + if let Some(fb) = storage.fallback_storage.take() { + storage.storage = Box::new(fb); + match storage.storage.store(creds.clone()) { + Err(e) => { + warning!(self.log, "Also failed to update fallback storage: {}", e) + } + Ok(_) => debug!(self.log, "Updated fallback storage successfully"), + } + } } + storage.last_read.set(Ok(Some(creds))); }) } diff --git a/cli/src/bin/code/main.rs b/cli/src/bin/code/main.rs index d000998c7dc..8c32ee14d89 100644 --- a/cli/src/bin/code/main.rs +++ b/cli/src/bin/code/main.rs @@ -95,7 +95,9 @@ async fn main() -> Result<(), std::convert::Infallible> { args::VersionSubcommand::Show => version::show(context!()).await, }, - Some(args::Commands::CommandShell) => tunnels::command_shell(context!()).await, + Some(args::Commands::CommandShell(cs_args)) => { + tunnels::command_shell(context!(), cs_args).await + } Some(args::Commands::Tunnel(tunnel_args)) => match tunnel_args.subcommand { Some(args::TunnelSubcommand::Prune) => tunnels::prune(context!()).await, @@ -112,6 +114,9 @@ async fn main() -> Result<(), std::convert::Infallible> { Some(args::TunnelSubcommand::Service(service_args)) => { tunnels::service(context_no_logger(), service_args).await } + Some(args::TunnelSubcommand::ForwardInternal(forward_args)) => { + tunnels::forward(context_no_logger(), forward_args).await + } None => tunnels::serve(context_no_logger(), tunnel_args.serve_args).await, }, }, diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index ad961496bcc..0051f72f395 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -174,7 +174,20 @@ pub enum Commands { /// Runs the control server on process stdin/stdout #[clap(hide = true)] - CommandShell, + CommandShell(CommandShellArgs), +} + +#[derive(Args, Debug, Clone)] +pub struct CommandShellArgs { + /// Listen on a socket instead of stdin/stdout. + #[clap(long)] + pub on_socket: bool, + /// Listen on a port instead of stdin/stdout. + #[clap(long)] + pub on_port: bool, + /// Require the given token string to be given in the handshake. + #[clap(long)] + pub require_token: Option, } #[derive(Args, Debug, Clone)] @@ -548,6 +561,7 @@ pub enum OutputFormat { #[derive(Args, Clone, Debug, Default)] pub struct ExistingTunnelArgs { /// Name you'd like to assign preexisting tunnel to use to connect the tunnel + /// Old option, new code sohuld just use `--name`. #[clap(long, hide = true)] pub tunnel_name: Option, @@ -626,6 +640,10 @@ pub enum TunnelSubcommand { /// (Preview) Manages the tunnel when installed as a system service, #[clap(subcommand)] Service(TunnelServiceSubCommands), + + /// (Preview) Forwards local port using the dev tunnel + #[clap(hide = true)] + ForwardInternal(TunnelForwardArgs), } #[derive(Subcommand, Debug, Clone)] @@ -649,6 +667,10 @@ pub struct TunnelServiceInstallArgs { /// If set, the user accepts the server license terms and the server will be started without a user prompt. #[clap(long)] pub accept_server_license_terms: bool, + + /// Sets the machine name for port forwarding service + #[clap(long)] + pub name: Option, } #[derive(Args, Debug, Clone)] @@ -657,6 +679,16 @@ pub struct TunnelRenameArgs { pub name: String, } +#[derive(Args, Debug, Clone)] +pub struct TunnelForwardArgs { + /// One or more ports to forward. + pub ports: Vec, + + /// Login args -- used for convenience so the forwarding call is a single action. + #[clap(flatten)] + pub login: LoginArgs, +} + #[derive(Subcommand, Debug, Clone)] pub enum TunnelUserSubCommands { /// Log in to port forwarding service diff --git a/cli/src/commands/tunnels.rs b/cli/src/commands/tunnels.rs index d11c15ef1e3..95f9b12a3d4 100644 --- a/cli/src/commands/tunnels.rs +++ b/cli/src/commands/tunnels.rs @@ -5,27 +5,37 @@ use async_trait::async_trait; use base64::{engine::general_purpose as b64, Engine as _}; +use futures::{stream::FuturesUnordered, StreamExt}; use serde::Serialize; use sha2::{Digest, Sha256}; use std::{str::FromStr, time::Duration}; use sysinfo::Pid; +use tokio::{ + io::{AsyncBufReadExt, BufReader}, + sync::watch, +}; use super::{ args::{ - AuthProvider, CliCore, ExistingTunnelArgs, TunnelRenameArgs, TunnelServeArgs, - TunnelServiceSubCommands, TunnelUserSubCommands, + AuthProvider, CliCore, CommandShellArgs, ExistingTunnelArgs, TunnelForwardArgs, + TunnelRenameArgs, TunnelServeArgs, TunnelServiceSubCommands, TunnelUserSubCommands, }, CommandContext, }; use crate::{ + async_pipe::{get_socket_name, listen_socket_rw_stream, AsyncRWAccepter}, auth::Auth, - constants::{APPLICATION_NAME, TUNNEL_CLI_LOCK_NAME, TUNNEL_SERVICE_LOCK_NAME}, + constants::{ + APPLICATION_NAME, CONTROL_PORT, IS_A_TTY, TUNNEL_CLI_LOCK_NAME, TUNNEL_SERVICE_LOCK_NAME, + }, log, state::LauncherPaths, tunnels::{ code_server::CodeServerArgs, - create_service_manager, dev_tunnels, legal, + create_service_manager, + dev_tunnels::{self, DevTunnels}, + forwarding, legal, paths::get_all_servers, protocol, serve_stream, shutdown_signal::ShutdownRequest, @@ -33,7 +43,7 @@ use crate::{ singleton_server::{ make_singleton_server, start_singleton_server, BroadcastLogSink, SingletonServerArgs, }, - Next, ServeStreamParams, ServiceContainer, ServiceManager, + AuthRequired, Next, ServeStreamParams, ServiceContainer, ServiceManager, }, util::{ app_lock::AppMutex, @@ -59,20 +69,31 @@ impl From for crate::auth::AuthProvider { } } -impl From for Option { - fn from(d: ExistingTunnelArgs) -> Option { - if let (Some(tunnel_id), Some(tunnel_name), Some(cluster), Some(host_token)) = - (d.tunnel_id, d.tunnel_name, d.cluster, d.host_token) - { +fn fulfill_existing_tunnel_args( + d: ExistingTunnelArgs, + name_arg: &Option, +) -> Option { + let tunnel_name = d.tunnel_name.or_else(|| name_arg.clone()); + + match (d.tunnel_id, d.cluster, d.host_token) { + (Some(tunnel_id), None, Some(host_token)) => { + let i = tunnel_id.find('.')?; Some(dev_tunnels::ExistingTunnel { - tunnel_id, + tunnel_id: tunnel_id[..i].to_string(), + cluster: tunnel_id[i + 1..].to_string(), tunnel_name, host_token, - cluster, }) - } else { - None } + + (Some(tunnel_id), Some(cluster), Some(host_token)) => Some(dev_tunnels::ExistingTunnel { + tunnel_id, + tunnel_name, + host_token, + cluster, + }), + + _ => None, } } @@ -109,23 +130,71 @@ impl ServiceContainer for TunnelServiceContainer { } } -pub async fn command_shell(ctx: CommandContext) -> Result { +pub async fn command_shell(ctx: CommandContext, args: CommandShellArgs) -> Result { let platform = PreReqChecker::new().verify().await?; - serve_stream( - tokio::io::stdin(), - tokio::io::stderr(), - ServeStreamParams { - log: ctx.log, - launcher_paths: ctx.paths, - platform, - requires_auth: true, - exit_barrier: ShutdownRequest::create_rx([ShutdownRequest::CtrlC]), - code_server_args: (&ctx.args).into(), - }, - ) - .await; + let mut params = ServeStreamParams { + log: ctx.log, + launcher_paths: ctx.paths, + platform, + requires_auth: args + .require_token + .map(AuthRequired::VSDAWithToken) + .unwrap_or(AuthRequired::VSDA), + exit_barrier: ShutdownRequest::create_rx([ShutdownRequest::CtrlC]), + code_server_args: (&ctx.args).into(), + }; - Ok(0) + let mut listener: Box = match (args.on_port, args.on_socket) { + (_, true) => { + let socket = get_socket_name(); + let listener = listen_socket_rw_stream(&socket) + .await + .map_err(|e| wrap(e, "error listening on socket"))?; + + params + .log + .result(format!("Listening on {}", socket.display())); + + Box::new(listener) + } + (true, _) => { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .map_err(|e| wrap(e, "error listening on port"))?; + + params + .log + .result(format!("Listening on {}", listener.local_addr().unwrap())); + + Box::new(listener) + } + _ => { + serve_stream(tokio::io::stdin(), tokio::io::stderr(), params).await; + return Ok(0); + } + }; + + let mut servers = FuturesUnordered::new(); + + loop { + tokio::select! { + Some(_) = servers.next() => {}, + socket = listener.accept_rw() => { + match socket { + Ok((read, write)) => servers.push(serve_stream(read, write, params.clone())), + Err(e) => { + error!(params.log, &format!("Error accepting connection: {}", e)); + return Ok(1); + } + } + }, + _ = params.exit_barrier.wait() => { + // wait for all servers to finish up: + while (servers.next().await).is_some() { } + return Ok(0); + } + } + } } pub async fn service( @@ -135,10 +204,17 @@ pub async fn service( let manager = create_service_manager(ctx.log.clone(), &ctx.paths); match service_args { TunnelServiceSubCommands::Install(args) => { - // ensure logged in, otherwise subsequent serving will fail - Auth::new(&ctx.paths, ctx.log.clone()) - .get_credential() - .await?; + let auth = Auth::new(&ctx.paths, ctx.log.clone()); + + if let Some(name) = &args.name { + // ensure the name matches, and tunnel exists + dev_tunnels::DevTunnels::new_remote_tunnel(&ctx.log, auth, &ctx.paths) + .rename_tunnel(name) + .await?; + } else { + // still ensure they're logged in, otherwise subsequent serving will fail + auth.get_credential().await?; + } // likewise for license consent legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; @@ -203,23 +279,23 @@ pub async fn user(ctx: CommandContext, user_args: TunnelUserSubCommands) -> Resu Ok(0) } -/// Remove the tunnel used by this gateway, if any. +/// Remove the tunnel used by this tunnel, if any. pub async fn rename(ctx: CommandContext, rename_args: TunnelRenameArgs) -> Result { let auth = Auth::new(&ctx.paths, ctx.log.clone()); - let mut dt = dev_tunnels::DevTunnels::new(&ctx.log, auth, &ctx.paths); + let mut dt = dev_tunnels::DevTunnels::new_remote_tunnel(&ctx.log, auth, &ctx.paths); dt.rename_tunnel(&rename_args.name).await?; ctx.log.result(format!( - "Successfully renamed this gateway to {}", + "Successfully renamed this tunnel to {}", &rename_args.name )); Ok(0) } -/// Remove the tunnel used by this gateway, if any. +/// Remove the tunnel used by this tunnel, if any. pub async fn unregister(ctx: CommandContext) -> Result { let auth = Auth::new(&ctx.paths, ctx.log.clone()); - let mut dt = dev_tunnels::DevTunnels::new(&ctx.log, auth, &ctx.paths); + let mut dt = dev_tunnels::DevTunnels::new_remote_tunnel(&ctx.log, auth, &ctx.paths); dt.remove_tunnel().await?; Ok(0) } @@ -327,6 +403,88 @@ pub async fn serve(ctx: CommandContext, gateway_args: TunnelServeArgs) -> Result result } +/// Internal command used by port forwarding. It reads requests for forwarded ports +/// on lines from stdin, as JSON. It uses singleton logic as well (though on +/// a different tunnel than the main one used for the control server) so that +/// all forward requests on a single machine go through a single hosted tunnel +/// process. Without singleton logic, requests could get routed to processes +/// that aren't forwarding a given port and then fail. +pub async fn forward( + ctx: CommandContext, + mut forward_args: TunnelForwardArgs, +) -> Result { + // Spooky: check IS_A_TTY before starting the stdin reader, since IS_A_TTY will + // access stdin but a lock will later be held on stdin by the line-reader. + if *IS_A_TTY { + trace!(ctx.log, "port forwarding is an internal preview feature"); + } + + // #region stdin reading logic: + let (own_ports_tx, own_ports_rx) = watch::channel(vec![]); + let ports_process_log = ctx.log.clone(); + tokio::spawn(async move { + let mut lines = BufReader::new(tokio::io::stdin()).lines(); + while let Ok(Some(line)) = lines.next_line().await { + match serde_json::from_str(&line) { + Ok(p) => { + let _ = own_ports_tx.send(p); + } + Err(e) => warning!(ports_process_log, "error parsing ports: {}", e), + } + } + }); + + // #region singleton acquisition + let shutdown = ShutdownRequest::create_rx([ShutdownRequest::CtrlC]); + let server = loop { + if shutdown.is_open() { + return Ok(0); + } + + match acquire_singleton(&ctx.paths.forwarding_lockfile()).await { + Ok(SingletonConnection::Client(stream)) => { + debug!(ctx.log, "starting as client to singleton"); + let r = forwarding::client(forwarding::SingletonClientArgs { + log: ctx.log.clone(), + shutdown: shutdown.clone(), + stream, + port_requests: own_ports_rx.clone(), + }) + .await; + if let Err(e) = r { + warning!(ctx.log, "error contacting forwarding singleton: {}", e); + } + } + Ok(SingletonConnection::Singleton(server)) => break server, + Err(e) => { + warning!(ctx.log, "error access singleton, retrying: {}", e); + tokio::time::sleep(Duration::from_secs(2)).await + } + } + }; + + // #region singleton handler + let auth = Auth::new(&ctx.paths, ctx.log.clone()); + println!("preauth {:?}", forward_args.login); + if let (Some(p), Some(at)) = ( + forward_args.login.provider.take(), + forward_args.login.access_token.take(), + ) { + auth.login(Some(p.into()), Some(at)).await?; + } + println!("auth done"); + + let mut tunnels = DevTunnels::new_port_forwarding(&ctx.log, auth, &ctx.paths); + let tunnel = tunnels + .start_new_launcher_tunnel(None, true, &forward_args.ports) + .await?; + println!("made tunnel"); + + forwarding::server(ctx.log, tunnel, server, own_ports_rx, shutdown).await?; + + Ok(0) +} + fn get_connection_token(tunnel: &ActiveTunnel) -> String { let mut hash = Sha256::new(); hash.update(tunnel.id.as_bytes()); @@ -374,7 +532,7 @@ async fn serve_with_csa( return Ok(0); } - match acquire_singleton(paths.tunnel_lockfile()).await { + match acquire_singleton(&paths.tunnel_lockfile()).await { Ok(SingletonConnection::Client(stream)) => { debug!(log, "starting as client to singleton"); let should_exit = start_singleton_client(SingletonClientArgs { @@ -403,13 +561,19 @@ async fn serve_with_csa( let _lock = app_mutex_name.map(AppMutex::new); let auth = Auth::new(&paths, log.clone()); - let mut dt = dev_tunnels::DevTunnels::new(&log, auth, &paths); + let mut dt = dev_tunnels::DevTunnels::new_remote_tunnel(&log, auth, &paths); loop { - let tunnel = if let Some(d) = gateway_args.tunnel.clone().into() { - dt.start_existing_tunnel(d).await + let tunnel = if let Some(t) = + fulfill_existing_tunnel_args(gateway_args.tunnel.clone(), &gateway_args.name) + { + dt.start_existing_tunnel(t).await } else { - dt.start_new_launcher_tunnel(gateway_args.name.as_deref(), gateway_args.random_name) - .await + dt.start_new_launcher_tunnel( + gateway_args.name.as_deref(), + gateway_args.random_name, + &[CONTROL_PORT], + ) + .await }?; csa.connection_token = Some(get_connection_token(&tunnel)); diff --git a/cli/src/msgpack_rpc.rs b/cli/src/msgpack_rpc.rs index 219c923cdf2..ef6b7782074 100644 --- a/cli/src/msgpack_rpc.rs +++ b/cli/src/msgpack_rpc.rs @@ -122,7 +122,7 @@ pub struct MsgPackCodec { impl MsgPackCodec { pub fn new() -> Self { Self { - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, } } } diff --git a/cli/src/rpc.rs b/cli/src/rpc.rs index acd53dc38e0..a9a66153735 100644 --- a/cli/src/rpc.rs +++ b/cli/src/rpc.rs @@ -117,7 +117,7 @@ impl RpcMethodBuilder { Ok(p) => p, Err(err) => { return id.map(|id| { - serial.serialize(&ErrorResponse { + serial.serialize(ErrorResponse { id, error: ResponseError { code: 0, @@ -131,7 +131,7 @@ impl RpcMethodBuilder { match callback(param.params, &context) { Ok(result) => id.map(|id| serial.serialize(&SuccessResponse { id, result })), Err(err) => id.map(|id| { - serial.serialize(&ErrorResponse { + serial.serialize(ErrorResponse { id, error: ResponseError { code: -1, @@ -161,7 +161,7 @@ impl RpcMethodBuilder { Ok(p) => p, Err(err) => { return future::ready(id.map(|id| { - serial.serialize(&ErrorResponse { + serial.serialize(ErrorResponse { id, error: ResponseError { code: 0, @@ -182,7 +182,7 @@ impl RpcMethodBuilder { id.map(|id| serial.serialize(&SuccessResponse { id, result })) } Err(err) => id.map(|id| { - serial.serialize(&ErrorResponse { + serial.serialize(ErrorResponse { id, error: ResponseError { code: -1, @@ -222,7 +222,7 @@ impl RpcMethodBuilder { return ( None, future::ready(id.map(|id| { - serial.serialize(&ErrorResponse { + serial.serialize(ErrorResponse { id, error: ResponseError { code: 0, @@ -255,7 +255,7 @@ impl RpcMethodBuilder { match callback(servers, param.params, context).await { Ok(r) => id.map(|id| serial.serialize(&SuccessResponse { id, result: r })), Err(err) => id.map(|id| { - serial.serialize(&ErrorResponse { + serial.serialize(ErrorResponse { id, error: ResponseError { code: -1, @@ -427,7 +427,7 @@ impl RpcDispatcher { Some(Method::Async(callback)) => MaybeSync::Future(callback(id, body)), Some(Method::Duplex(callback)) => MaybeSync::Stream(callback(id, body)), None => MaybeSync::Sync(id.map(|id| { - self.serializer.serialize(&ErrorResponse { + self.serializer.serialize(ErrorResponse { id, error: ResponseError { code: -1, diff --git a/cli/src/singleton.rs b/cli/src/singleton.rs index 0ea9cda2a8a..635c400fb0f 100644 --- a/cli/src/singleton.rs +++ b/cli/src/singleton.rs @@ -53,12 +53,12 @@ struct LockFileMatter { /// Tries to acquire the singleton homed at the given lock file, either starting /// a new singleton if it doesn't exist, or connecting otherwise. -pub async fn acquire_singleton(lock_file: PathBuf) -> Result { +pub async fn acquire_singleton(lock_file: &Path) -> Result { let file = OpenOptions::new() .read(true) .write(true) .create(true) - .open(&lock_file) + .open(lock_file) .map_err(CodeError::SingletonLockfileOpenFailed)?; match FileLock::acquire(file) { @@ -158,7 +158,7 @@ mod tests { #[tokio::test] async fn test_acquires_singleton() { let dir = tempfile::tempdir().expect("expected to make temp dir"); - let s = acquire_singleton(dir.path().join("lock")) + let s = acquire_singleton(&dir.path().join("lock")) .await .expect("expected to acquire"); @@ -172,7 +172,7 @@ mod tests { async fn test_acquires_client() { let dir = tempfile::tempdir().expect("expected to make temp dir"); let lockfile = dir.path().join("lock"); - let s1 = acquire_singleton(lockfile.clone()) + let s1 = acquire_singleton(&lockfile) .await .expect("expected to acquire1"); match s1 { @@ -182,7 +182,7 @@ mod tests { _ => panic!("expected to be singleton"), }; - let s2 = acquire_singleton(lockfile) + let s2 = acquire_singleton(&lockfile) .await .expect("expected to acquire2"); match s2 { diff --git a/cli/src/state.rs b/cli/src/state.rs index af0d18e160b..1b1ff343da5 100644 --- a/cli/src/state.rs +++ b/cli/src/state.rs @@ -187,6 +187,14 @@ impl LauncherPaths { )) } + /// Lockfile for port forwarding + pub fn forwarding_lockfile(&self) -> PathBuf { + self.root.join(format!( + "forwarding-{}.lock", + VSCODE_CLI_QUALITY.unwrap_or("oss") + )) + } + /// Suggested path for tunnel service logs, when using file logs pub fn service_log_file(&self) -> PathBuf { self.root.join("tunnel-service.log") diff --git a/cli/src/tunnels.rs b/cli/src/tunnels.rs index 5d97b757afc..700516abb1f 100644 --- a/cli/src/tunnels.rs +++ b/cli/src/tunnels.rs @@ -11,6 +11,7 @@ pub mod protocol; pub mod shutdown_signal; pub mod singleton_client; pub mod singleton_server; +pub mod forwarding; mod wsl_detect; mod challenge; @@ -34,7 +35,7 @@ mod service_macos; mod service_windows; mod socket_signal; -pub use control_server::{serve, serve_stream, Next, ServeStreamParams}; +pub use control_server::{serve, serve_stream, Next, ServeStreamParams, AuthRequired}; pub use nosleep::SleepInhibitor; pub use service::{ create_service_manager, ServiceContainer, ServiceManager, SERVICE_LOG_FILE_NAME, diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index fe257a64314..8ff9e640d13 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -575,7 +575,17 @@ impl<'a> ServerBuilder<'a> { } fn get_base_command(&self) -> Command { + #[cfg(not(windows))] let mut cmd = Command::new(&self.server_paths.executable); + #[cfg(windows)] + let mut cmd = { + let mut cmd = Command::new("cmd"); + cmd.arg("/Q"); + cmd.arg("/C"); + cmd.arg(&self.server_paths.executable); + cmd + }; + cmd.stdin(std::process::Stdio::null()) .args(self.server_params.code_server_args.command_arguments()); cmd diff --git a/cli/src/tunnels/control_server.rs b/cli/src/tunnels/control_server.rs index e0c1ec19fc2..6f8c1060e1f 100644 --- a/cli/src/tunnels/control_server.rs +++ b/cli/src/tunnels/control_server.rs @@ -48,11 +48,11 @@ use super::dev_tunnels::ActiveTunnel; use super::paths::prune_stopped_servers; use super::port_forwarder::{PortForwarding, PortForwardingProcessor}; use super::protocol::{ - AcquireCliParams, CallServerHttpParams, CallServerHttpResult, ChallengeIssueResponse, - ChallengeVerifyParams, ClientRequestMethod, EmptyObject, ForwardParams, ForwardResult, - FsStatRequest, FsStatResponse, GetEnvResponse, GetHostnameResponse, HttpBodyParams, - HttpHeadersParams, ServeParams, ServerLog, ServerMessageParams, SpawnParams, SpawnResult, - ToClientRequest, UnforwardParams, UpdateParams, UpdateResult, VersionResponse, + AcquireCliParams, CallServerHttpParams, CallServerHttpResult, ChallengeIssueParams, + ChallengeIssueResponse, ChallengeVerifyParams, ClientRequestMethod, EmptyObject, ForwardParams, + ForwardResult, FsStatRequest, FsStatResponse, GetEnvResponse, GetHostnameResponse, + HttpBodyParams, HttpHeadersParams, ServeParams, ServerLog, ServerMessageParams, SpawnParams, + SpawnResult, ToClientRequest, UnforwardParams, UpdateParams, UpdateResult, VersionResponse, METHOD_CHALLENGE_VERIFY, }; use super::server_bridge::ServerBridge; @@ -94,8 +94,8 @@ struct HandlerContext { /// Handler auth state. enum AuthState { - /// Auth is required, we're waiting for the client to send its challenge. - WaitingForChallenge, + /// Auth is required, we're waiting for the client to send its challenge optionally bearing a token. + WaitingForChallenge(Option), /// A challenge has been issued. Waiting for a verification. ChallengeIssued(String), /// Auth is no longer required. @@ -215,7 +215,7 @@ pub async fn serve( code_server_args: own_code_server_args, platform, exit_barrier: own_exit, - requires_auth: false, + requires_auth: AuthRequired::None, }).with_context(cx.clone()).await; cx.span().add_event( @@ -233,12 +233,20 @@ pub async fn serve( } } +#[derive(Clone)] +pub enum AuthRequired { + None, + VSDA, + VSDAWithToken(String), +} + +#[derive(Clone)] pub struct ServeStreamParams { pub log: log::Logger, pub launcher_paths: LauncherPaths, pub code_server_args: CodeServerArgs, pub platform: Platform, - pub requires_auth: bool, + pub requires_auth: AuthRequired, pub exit_barrier: Barrier, } @@ -268,7 +276,7 @@ fn make_socket_rpc( launcher_paths: LauncherPaths, code_server_args: CodeServerArgs, port_forwarding: Option, - requires_auth: bool, + requires_auth: AuthRequired, platform: Platform, ) -> RpcDispatcher { let http_requests = Arc::new(std::sync::Mutex::new(HashMap::new())); @@ -276,8 +284,9 @@ fn make_socket_rpc( let mut rpc = RpcBuilder::new(MsgPackSerializer {}).methods(HandlerContext { did_update: Arc::new(AtomicBool::new(false)), auth_state: Arc::new(std::sync::Mutex::new(match requires_auth { - true => AuthState::WaitingForChallenge, - false => AuthState::Authenticated, + AuthRequired::VSDAWithToken(t) => AuthState::WaitingForChallenge(Some(t)), + AuthRequired::VSDA => AuthState::WaitingForChallenge(None), + AuthRequired::None => AuthState::Authenticated, })), socket_tx, log: log.clone(), @@ -304,8 +313,8 @@ fn make_socket_rpc( ensure_auth(&c.auth_state)?; handle_get_env() }); - rpc.register_sync(METHOD_CHALLENGE_ISSUE, |_: EmptyObject, c| { - handle_challenge_issue(&c.auth_state) + rpc.register_sync(METHOD_CHALLENGE_ISSUE, |p: ChallengeIssueParams, c| { + handle_challenge_issue(p, &c.auth_state) }); rpc.register_sync(METHOD_CHALLENGE_VERIFY, |p: ChallengeVerifyParams, c| { handle_challenge_verify(p.response, &c.auth_state) @@ -422,6 +431,7 @@ async fn process_socket( let rx_counter = Arc::new(AtomicUsize::new(0)); let http_requests = Arc::new(std::sync::Mutex::new(HashMap::new())); + let already_authed = matches!(requires_auth, AuthRequired::None); let rpc = make_socket_rpc( log.clone(), socket_tx.clone(), @@ -439,7 +449,7 @@ async fn process_socket( let socket_tx = socket_tx.clone(); let exit_barrier = exit_barrier.clone(); tokio::spawn(async move { - if !requires_auth { + if already_authed { send_version(&socket_tx).await; } @@ -825,13 +835,22 @@ fn handle_get_env() -> Result { } fn handle_challenge_issue( + params: ChallengeIssueParams, auth_state: &Arc>, ) -> Result { let challenge = create_challenge(); let mut auth_state = auth_state.lock().unwrap(); - *auth_state = AuthState::ChallengeIssued(challenge.clone()); + if let AuthState::WaitingForChallenge(Some(s)) = &*auth_state { + println!("looking for token {}, got {:?}", s, params.token); + match ¶ms.token { + Some(t) if s != t => return Err(CodeError::AuthChallengeBadToken.into()), + None => return Err(CodeError::AuthChallengeBadToken.into()), + _ => {} + } + } + *auth_state = AuthState::ChallengeIssued(challenge.clone()); Ok(ChallengeIssueResponse { challenge }) } @@ -843,7 +862,7 @@ fn handle_challenge_verify( match &*auth_state { AuthState::Authenticated => Ok(EmptyObject {}), - AuthState::WaitingForChallenge => Err(CodeError::AuthChallengeNotIssued.into()), + AuthState::WaitingForChallenge(_) => Err(CodeError::AuthChallengeNotIssued.into()), AuthState::ChallengeIssued(c) => match verify_challenge(c, &response) { false => Err(CodeError::AuthChallengeNotIssued.into()), true => { diff --git a/cli/src/tunnels/dev_tunnels.rs b/cli/src/tunnels/dev_tunnels.rs index 8476028a2f5..3bdc5cf189b 100644 --- a/cli/src/tunnels/dev_tunnels.rs +++ b/cli/src/tunnels/dev_tunnels.rs @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ use crate::auth; -use crate::constants::{ - CONTROL_PORT, IS_INTERACTIVE_CLI, PROTOCOL_VERSION_TAG, TUNNEL_SERVICE_USER_AGENT, -}; +use crate::constants::{IS_INTERACTIVE_CLI, PROTOCOL_VERSION_TAG, TUNNEL_SERVICE_USER_AGENT}; use crate::state::{LauncherPaths, PersistedState}; use crate::util::errors::{ - wrap, AnyError, DevTunnelError, InvalidTunnelName, TunnelCreationFailed, WrappedError, + wrap, AnyError, CodeError, DevTunnelError, InvalidTunnelName, TunnelCreationFailed, + WrappedError, }; use crate::util::input::prompt_placeholder; use crate::{debug, info, log, spanf, trace, warning}; @@ -33,6 +32,8 @@ use tunnels::management::{ use super::wsl_detect::is_wsl_installed; +static TUNNEL_COUNT_LIMIT_NAME: &str = "TunnelsPerUserPerLocation"; + #[derive(Clone, Serialize, Deserialize)] pub struct PersistedTunnel { pub name: String, @@ -134,6 +135,7 @@ pub struct DevTunnels { log: log::Logger, launcher_tunnel: PersistedState>, client: TunnelManagementClient, + tag: &'static str, } /// Representation of a tunnel returned from the `start` methods. @@ -162,30 +164,43 @@ impl ActiveTunnel { } /// Forwards a port over TCP. - pub async fn add_port_tcp(&mut self, port_number: u16) -> Result<(), AnyError> { + pub async fn add_port_tcp(&self, port_number: u16) -> Result<(), AnyError> { self.manager.add_port_tcp(port_number).await?; Ok(()) } /// Removes a forwarded port TCP. - pub async fn remove_port(&mut self, port_number: u16) -> Result<(), AnyError> { + pub async fn remove_port(&self, port_number: u16) -> Result<(), AnyError> { self.manager.remove_port(port_number).await?; Ok(()) } - /// Gets the public URI on which a forwarded port can be access in browser. - pub async fn get_port_uri(&mut self, port: u16) -> Result { - let endpoint = self.manager.get_endpoint().await?; - let format = endpoint - .base - .port_uri_format - .expect("expected to have port format"); + /// Gets the template string for forming forwarded port web URIs.. + pub fn get_port_format(&self) -> Result { + if let Some(details) = &*self.manager.endpoint_rx.borrow() { + return details + .as_ref() + .map(|r| { + r.base + .port_uri_format + .clone() + .expect("expected to have port format") + }) + .map_err(|e| e.clone().into()); + } - Ok(format.replace(PORT_TOKEN, &port.to_string())) + Err(CodeError::NoTunnelEndpoint.into()) + } + + /// Gets the public URI on which a forwarded port can be access in browser. + pub fn get_port_uri(&self, port: u16) -> Result { + self.get_port_format() + .map(|f| f.replace(PORT_TOKEN, &port.to_string())) } } const VSCODE_CLI_TUNNEL_TAG: &str = "vscode-server-launcher"; +const VSCODE_CLI_FORWARDING_TAG: &str = "vscode-port-forward"; const MAX_TUNNEL_NAME_LENGTH: usize = 20; fn get_host_token_from_tunnel(tunnel: &Tunnel) -> String { @@ -229,7 +244,7 @@ lazy_static! { #[derive(Clone, Debug)] pub struct ExistingTunnel { /// Name you'd like to assign preexisting tunnel to use to connect to the VS Code Server - pub tunnel_name: String, + pub tunnel_name: Option, /// Token to authenticate and use preexisting tunnel pub host_token: String, @@ -242,7 +257,29 @@ pub struct ExistingTunnel { } impl DevTunnels { - pub fn new(log: &log::Logger, auth: auth::Auth, paths: &LauncherPaths) -> DevTunnels { + /// Creates a new DevTunnels client used for port forwarding. + pub fn new_port_forwarding( + log: &log::Logger, + auth: auth::Auth, + paths: &LauncherPaths, + ) -> DevTunnels { + let mut client = new_tunnel_management(&TUNNEL_SERVICE_USER_AGENT); + client.authorization_provider(auth); + + DevTunnels { + log: log.clone(), + client: client.into(), + launcher_tunnel: PersistedState::new(paths.root().join("port_forwarding_tunnel.json")), + tag: VSCODE_CLI_FORWARDING_TAG, + } + } + + /// Creates a new DevTunnels client used for the Remote Tunnels extension to access the VS Code Server. + pub fn new_remote_tunnel( + log: &log::Logger, + auth: auth::Auth, + paths: &LauncherPaths, + ) -> DevTunnels { let mut client = new_tunnel_management(&TUNNEL_SERVICE_USER_AGENT); client.authorization_provider(auth); @@ -250,6 +287,7 @@ impl DevTunnels { log: log.clone(), client: client.into(), launcher_tunnel: PersistedState::new(paths.root().join("code_tunnel.json")), + tag: VSCODE_CLI_TUNNEL_TAG, } } @@ -275,7 +313,9 @@ impl DevTunnels { /// Renames the current tunnel to the new name. pub async fn rename_tunnel(&mut self, name: &str) -> Result<(), AnyError> { - self.update_tunnel_name(None, name).await.map(|_| ()) + self.update_tunnel_name(self.launcher_tunnel.load(), name) + .await + .map(|_| ()) } /// Updates the name of the existing persisted tunnel to the new name. @@ -286,28 +326,34 @@ impl DevTunnels { name: &str, ) -> Result<(Tunnel, PersistedTunnel), AnyError> { let name = name.to_ascii_lowercase(); - self.check_is_name_free(&name).await?; - - debug!(self.log, "Tunnel name changed, applying updates..."); let (mut full_tunnel, mut persisted, is_new) = match persisted { Some(persisted) => { + debug!( + self.log, + "Found a persisted tunnel, seeing if the name matches..." + ); self.get_or_create_tunnel(persisted, Some(&name), NO_REQUEST_OPTIONS) .await } - None => self - .create_tunnel(&name, NO_REQUEST_OPTIONS) - .await - .map(|(pt, t)| (t, pt, true)), + None => { + debug!(self.log, "Creating a new tunnel with the requested name"); + self.create_tunnel(&name, NO_REQUEST_OPTIONS) + .await + .map(|(pt, t)| (t, pt, true)) + } }?; - if is_new { + let desired_tags = self.get_tags(&name); + if is_new || vec_eq_as_set(&full_tunnel.tags, &desired_tags) { return Ok((full_tunnel, persisted)); } - full_tunnel.tags = self.get_tags(&name); + debug!(self.log, "Tunnel name changed, applying updates..."); - let new_tunnel = spanf!( + full_tunnel.tags = desired_tags; + + let updated_tunnel = spanf!( self.log, self.log.span("dev-tunnel.tag.update"), self.client.update_tunnel(&full_tunnel, NO_REQUEST_OPTIONS) @@ -317,7 +363,7 @@ impl DevTunnels { persisted.name = name; self.launcher_tunnel.save(Some(persisted.clone()))?; - Ok((new_tunnel, persisted)) + Ok((updated_tunnel, persisted)) } /// Gets the persisted tunnel from the service, or creates a new one. @@ -356,6 +402,7 @@ impl DevTunnels { &mut self, preferred_name: Option<&str>, use_random_name: bool, + preserve_ports: &[u16], ) -> Result { let (mut tunnel, persisted) = match self.launcher_tunnel.load() { Some(mut persisted) => { @@ -385,7 +432,12 @@ impl DevTunnels { }; tunnel = self - .sync_tunnel_tags(&persisted.name, tunnel, &HOST_TUNNEL_REQUEST_OPTIONS) + .sync_tunnel_tags( + &self.client, + &persisted.name, + tunnel, + &HOST_TUNNEL_REQUEST_OPTIONS, + ) .await?; let locator = TunnelLocator::try_from(&tunnel).unwrap(); @@ -394,7 +446,7 @@ impl DevTunnels { for port_to_delete in tunnel .ports .iter() - .filter(|p| p.port_number != CONTROL_PORT) + .filter(|p: &&TunnelPort| !preserve_ports.contains(&p.port_number)) { let output_fut = self.client.delete_tunnel_port( &locator, @@ -443,14 +495,10 @@ impl DevTunnels { ) -> Result<(PersistedTunnel, Tunnel), AnyError> { info!(self.log, "Creating tunnel with the name: {}", name); - let mut tried_recycle = false; + self.check_is_name_free(name).await?; let new_tunnel = Tunnel { - tags: vec![ - name.to_string(), - PROTOCOL_VERSION_TAG.to_string(), - VSCODE_CLI_TUNNEL_TAG.to_string(), - ], + tags: self.get_tags(name), ..Default::default() }; @@ -465,13 +513,14 @@ impl DevTunnels { Err(HttpError::ResponseError(e)) if e.status_code == StatusCode::TOO_MANY_REQUESTS => { - if !tried_recycle && self.try_recycle_tunnel().await? { - tried_recycle = true; - continue; - } - if let Some(d) = e.get_details() { let detail = d.detail.unwrap_or_else(|| "unknown".to_string()); + if detail.contains(TUNNEL_COUNT_LIMIT_NAME) + && self.try_recycle_tunnel().await? + { + continue; + } + return Err(AnyError::from(TunnelCreationFailed( name.to_string(), detail, @@ -508,7 +557,7 @@ impl DevTunnels { let mut tags = vec![ name.to_string(), PROTOCOL_VERSION_TAG.to_string(), - VSCODE_CLI_TUNNEL_TAG.to_string(), + self.tag.to_string(), ]; if is_wsl_installed(&self.log) { @@ -522,12 +571,13 @@ impl DevTunnels { /// other version tags. async fn sync_tunnel_tags( &self, + client: &TunnelManagementClient, name: &str, tunnel: Tunnel, options: &TunnelRequestOptions, ) -> Result { let new_tags = self.get_tags(name); - if vec_eq_unsorted(&tunnel.tags, &new_tags) { + if vec_eq_as_set(&tunnel.tags, &new_tags) { return Ok(tunnel); } @@ -548,7 +598,7 @@ impl DevTunnels { let result = spanf!( self.log, self.log.span("dev-tunnel.protocol-tag-update"), - self.client.update_tunnel(&tunnel_update, options) + client.update_tunnel(&tunnel_update, options) ); result.map_err(|e| wrap(e, "tunnel tag update failed").into()) @@ -599,7 +649,7 @@ impl DevTunnels { self.log, self.log.span("dev-tunnel.listall"), self.client.list_all_tunnels(&TunnelRequestOptions { - tags: vec![VSCODE_CLI_TUNNEL_TAG.to_string()], + tags: vec![self.tag.to_string()], require_all_tags: true, ..Default::default() }) @@ -610,11 +660,11 @@ impl DevTunnels { } async fn check_is_name_free(&mut self, name: &str) -> Result<(), AnyError> { - let existing = spanf!( + let existing: Vec = spanf!( self.log, self.log.span("dev-tunnel.rename.search"), self.client.list_all_tunnels(&TunnelRequestOptions { - tags: vec![VSCODE_CLI_TUNNEL_TAG.to_string(), name.to_string()], + tags: vec![self.tag.to_string(), name.to_string()], require_all_tags: true, ..Default::default() }) @@ -629,6 +679,12 @@ impl DevTunnels { Ok(()) } + fn get_placeholder_name() -> String { + let mut n = clean_hostname_for_tunnel(&gethostname::gethostname().to_string_lossy()); + n.make_ascii_lowercase(); + n + } + async fn get_name_for_tunnel( &mut self, preferred_name: Option<&str>, @@ -660,10 +716,7 @@ impl DevTunnels { use_random_name = true; } - let mut placeholder_name = - clean_hostname_for_tunnel(&gethostname::gethostname().to_string_lossy()); - placeholder_name.make_ascii_lowercase(); - + let mut placeholder_name = Self::get_placeholder_name(); if !is_name_free(&placeholder_name) { for i in 2.. { let fixed_name = format!("{}{}", placeholder_name, i); @@ -705,7 +758,10 @@ impl DevTunnels { tunnel: ExistingTunnel, ) -> Result { let tunnel_details = PersistedTunnel { - name: tunnel.tunnel_name, + name: match tunnel.tunnel_name { + Some(n) => n, + None => Self::get_placeholder_name(), + }, id: tunnel.tunnel_id, cluster: tunnel.cluster, }; @@ -715,10 +771,23 @@ impl DevTunnels { tunnel.host_token.clone(), )); + let client = mgmt.into(); + self.sync_tunnel_tags( + &client, + &tunnel_details.name, + Tunnel { + cluster_id: Some(tunnel_details.cluster.clone()), + tunnel_id: Some(tunnel_details.id.clone()), + ..Default::default() + }, + &HOST_TUNNEL_REQUEST_OPTIONS, + ) + .await?; + self.start_tunnel( tunnel_details.locator(), &tunnel_details, - mgmt.into(), + client, StaticAccessTokenProvider::new(tunnel.host_token), ) .await @@ -998,7 +1067,7 @@ fn clean_hostname_for_tunnel(hostname: &str) -> String { } } -fn vec_eq_unsorted(a: &[String], b: &[String]) -> bool { +fn vec_eq_as_set(a: &[String], b: &[String]) -> bool { if a.len() != b.len() { return false; } diff --git a/cli/src/tunnels/forwarding.rs b/cli/src/tunnels/forwarding.rs new file mode 100644 index 00000000000..1557b97c04e --- /dev/null +++ b/cli/src/tunnels/forwarding.rs @@ -0,0 +1,284 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use tokio::{ + pin, + sync::{mpsc, watch}, +}; + +use crate::{ + async_pipe::{socket_stream_split, AsyncPipe}, + json_rpc::{new_json_rpc, start_json_rpc}, + log, + singleton::SingletonServer, + util::{errors::CodeError, sync::Barrier}, +}; + +use super::{ + dev_tunnels::ActiveTunnel, + protocol::{ + self, + forward_singleton::{PortList, SetPortsResponse}, + }, + shutdown_signal::ShutdownSignal, +}; + +type PortMap = HashMap; + +/// The PortForwardingHandle is given out to multiple consumers to allow +/// them to set_ports that they want to be forwarded. +struct PortForwardingSender { + /// Todo: when `SyncUnsafeCell` is no longer nightly, we can use it here with + /// the following comment: + /// + /// SyncUnsafeCell is used and safe here because PortForwardingSender is used + /// exclusively in synchronous dispatch *and* we create a new sender in the + /// context for each connection, in `serve_singleton_rpc`. + /// + /// If PortForwardingSender is ever used in a different context, this should + /// be refactored, e.g. to use locks or `&mut self` in set_ports` + /// + /// see https://doc.rust-lang.org/stable/std/cell/struct.SyncUnsafeCell.html + current: Mutex, + sender: Arc>>, +} + +impl PortForwardingSender { + pub fn set_ports(&self, ports: PortList) { + let mut current = self.current.lock().unwrap(); + self.sender.lock().unwrap().send_modify(|v| { + for p in current.iter() { + if !ports.contains(p) { + match v.get(p) { + Some(1) => { + v.remove(p); + } + Some(n) => { + v.insert(*p, n - 1); + } + None => unreachable!("removed port not in map"), + } + } + } + + for p in ports.iter() { + if !current.contains(p) { + match v.get(p) { + Some(n) => v.insert(*p, n + 1), + None => v.insert(*p, 1), + }; + } + } + + current.splice(.., ports); + }); + } +} + +impl Clone for PortForwardingSender { + fn clone(&self) -> Self { + Self { + current: Mutex::new(vec![]), + sender: self.sender.clone(), + } + } +} + +impl Drop for PortForwardingSender { + fn drop(&mut self) { + self.set_ports(vec![]); + } +} + +struct PortForwardingReceiver { + receiver: watch::Receiver, +} + +impl PortForwardingReceiver { + pub fn new() -> (PortForwardingSender, Self) { + let (sender, receiver) = watch::channel(HashMap::new()); + let handle = PortForwardingSender { + current: Mutex::new(vec![]), + sender: Arc::new(Mutex::new(sender)), + }; + + let tracker = Self { receiver }; + + (handle, tracker) + } + + /// Applies all changes from PortForwardingHandles to the tunnel. + pub async fn apply_to(&mut self, log: log::Logger, tunnel: Arc) { + let mut current = vec![]; + while self.receiver.changed().await.is_ok() { + let next = self.receiver.borrow().keys().copied().collect::>(); + + for p in current.iter() { + if !next.contains(p) { + match tunnel.remove_port(*p).await { + Ok(_) => info!(log, "stopped forwarding port {}", p), + Err(e) => error!(log, "failed to stop forwarding port {}: {}", p, e), + } + } + } + for p in next.iter() { + if !current.contains(p) { + match tunnel.add_port_tcp(*p).await { + Ok(_) => info!(log, "forwarding port {}", p), + Err(e) => error!(log, "failed to forward port {}: {}", p, e), + } + } + } + + current = next; + } + } +} + +pub struct SingletonClientArgs { + pub log: log::Logger, + pub stream: AsyncPipe, + pub shutdown: Barrier, + pub port_requests: watch::Receiver, +} + +#[derive(Clone)] +struct SingletonServerContext { + log: log::Logger, + handle: PortForwardingSender, + tunnel: Arc, +} + +/// Serves a client singleton for port forwarding. +pub async fn client(args: SingletonClientArgs) -> Result<(), std::io::Error> { + let mut rpc = new_json_rpc(); + let (msg_tx, msg_rx) = mpsc::unbounded_channel(); + let SingletonClientArgs { + log, + shutdown, + stream, + mut port_requests, + } = args; + + debug!( + log, + "An existing port forwarding process is running on this machine, connecting to it..." + ); + + let caller = rpc.get_caller(msg_tx); + let rpc = rpc.methods(()).build(log.clone()); + let (read, write) = socket_stream_split(stream); + + let serve = start_json_rpc(rpc, read, write, msg_rx, shutdown); + let forward = async move { + while port_requests.changed().await.is_ok() { + let ports = port_requests.borrow().clone(); + let r = caller + .call::<_, _, protocol::forward_singleton::SetPortsResponse>( + protocol::forward_singleton::METHOD_SET_PORTS, + protocol::forward_singleton::SetPortsParams { ports }, + ) + .await + .unwrap(); + + match r { + Err(e) => error!(log, "failed to set ports: {:?}", e), + Ok(r) => print_forwarding_addr(&r), + }; + } + }; + + tokio::select! { + r = serve => r.map(|_| ()), + _ = forward => Ok(()), + } +} + +/// Serves a port-forwarding singleton. +pub async fn server( + log: log::Logger, + tunnel: ActiveTunnel, + server: SingletonServer, + mut port_requests: watch::Receiver, + shutdown_rx: Barrier, +) -> Result<(), CodeError> { + let tunnel = Arc::new(tunnel); + let (forward_tx, mut forward_rx) = PortForwardingReceiver::new(); + + let forward_own_tunnel = tunnel.clone(); + let forward_own_tx = forward_tx.clone(); + let forward_own = async move { + while port_requests.changed().await.is_ok() { + forward_own_tx.set_ports(port_requests.borrow().clone()); + print_forwarding_addr(&SetPortsResponse { + port_format: forward_own_tunnel.get_port_format().ok(), + }); + } + }; + + tokio::select! { + _ = forward_own => Ok(()), + _ = forward_rx.apply_to(log.clone(), tunnel.clone()) => Ok(()), + r = serve_singleton_rpc(server, log, tunnel, forward_tx, shutdown_rx) => r, + } +} + +async fn serve_singleton_rpc( + mut server: SingletonServer, + log: log::Logger, + tunnel: Arc, + forward_tx: PortForwardingSender, + shutdown_rx: Barrier, +) -> Result<(), CodeError> { + let mut own_shutdown = shutdown_rx.clone(); + let shutdown_fut = own_shutdown.wait(); + pin!(shutdown_fut); + + loop { + let cnx = tokio::select! { + c = server.accept() => c?, + _ = &mut shutdown_fut => return Ok(()), + }; + + let (read, write) = socket_stream_split(cnx); + let shutdown_rx = shutdown_rx.clone(); + + let handle = forward_tx.clone(); + let log = log.clone(); + let tunnel = tunnel.clone(); + tokio::spawn(async move { + // we make an rpc for the connection instead of re-using a dispatcher + // so that we can have the "handle" drop when the connection drops. + let rpc = new_json_rpc(); + let mut rpc = rpc.methods(SingletonServerContext { + log: log.clone(), + handle, + tunnel, + }); + + rpc.register_sync( + protocol::forward_singleton::METHOD_SET_PORTS, + |p: protocol::forward_singleton::SetPortsParams, ctx| { + info!(ctx.log, "client setting ports to {:?}", p.ports); + ctx.handle.set_ports(p.ports); + Ok(SetPortsResponse { + port_format: ctx.tunnel.get_port_format().ok(), + }) + }, + ); + + let _ = start_json_rpc(rpc.build(log), read, write, (), shutdown_rx).await; + }); + } +} + +fn print_forwarding_addr(r: &SetPortsResponse) { + eprintln!("{}\n", serde_json::to_string(r).unwrap()); +} diff --git a/cli/src/tunnels/paths.rs b/cli/src/tunnels/paths.rs index fa06db5dd7a..a0cd43cd83c 100644 --- a/cli/src/tunnels/paths.rs +++ b/cli/src/tunnels/paths.rs @@ -91,10 +91,15 @@ impl InstalledServer { pub fn server_paths(&self, p: &LauncherPaths) -> ServerPaths { let server_dir = self.get_install_folder(p); ServerPaths { - executable: server_dir - .join(SERVER_FOLDER_NAME) - .join("bin") - .join(self.quality.server_entrypoint()), + // allow using the OSS server in development via an override + executable: if let Some(p) = option_env!("VSCODE_CLI_OVERRIDE_SERVER_PATH") { + PathBuf::from(p) + } else { + server_dir + .join(SERVER_FOLDER_NAME) + .join("bin") + .join(self.quality.server_entrypoint()) + }, logfile: server_dir.join("log.txt"), pidfile: server_dir.join("pid.txt"), server_dir, diff --git a/cli/src/tunnels/port_forwarder.rs b/cli/src/tunnels/port_forwarder.rs index bb60a670dea..0f489f541d7 100644 --- a/cli/src/tunnels/port_forwarder.rs +++ b/cli/src/tunnels/port_forwarder.rs @@ -91,7 +91,7 @@ impl PortForwardingProcessor { self.forwarded.insert(port); } - tunnel.get_port_uri(port).await + tunnel.get_port_uri(port) } } diff --git a/cli/src/tunnels/protocol.rs b/cli/src/tunnels/protocol.rs index eb20afe0ce5..cf01917ee1e 100644 --- a/cli/src/tunnels/protocol.rs +++ b/cli/src/tunnels/protocol.rs @@ -199,6 +199,11 @@ pub struct SpawnResult { pub const METHOD_CHALLENGE_ISSUE: &str = "challenge_issue"; pub const METHOD_CHALLENGE_VERIFY: &str = "challenge_verify"; +#[derive(Serialize, Deserialize)] +pub struct ChallengeIssueParams { + pub token: Option, +} + #[derive(Serialize, Deserialize)] pub struct ChallengeIssueResponse { pub challenge: String, @@ -209,6 +214,24 @@ pub struct ChallengeVerifyParams { pub response: String, } +pub mod forward_singleton { + use serde::{Deserialize, Serialize}; + + pub const METHOD_SET_PORTS: &str = "set_ports"; + + pub type PortList = Vec; + + #[derive(Serialize, Deserialize)] + pub struct SetPortsParams { + pub ports: PortList, + } + + #[derive(Serialize, Deserialize)] + pub struct SetPortsResponse { + pub port_format: Option, + } +} + pub mod singleton { use crate::log; use serde::{Deserialize, Serialize}; diff --git a/cli/src/tunnels/service_windows.rs b/cli/src/tunnels/service_windows.rs index 427eddd620d..3d2dc9f0c55 100644 --- a/cli/src/tunnels/service_windows.rs +++ b/cli/src/tunnels/service_windows.rs @@ -78,6 +78,7 @@ impl CliServiceManager for WindowsService { cmd.stderr(Stdio::null()); cmd.stdout(Stdio::null()); cmd.stdin(Stdio::null()); + cmd.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); cmd.spawn() .map_err(|e| wrapdbg(e, "error starting service"))?; @@ -121,8 +122,12 @@ impl CliServiceManager for WindowsService { async fn unregister(&self) -> Result<(), AnyError> { let key = WindowsService::open_key()?; - key.delete_value(TUNNEL_ACTIVITY_NAME) - .map_err(|e| AnyError::from(wrap(e, "error deleting registry key")))?; + match key.delete_value(TUNNEL_ACTIVITY_NAME) { + Ok(_) => {} + Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} + Err(e) => return Err(wrap(e, "error deleting registry key").into()), + } + info!(self.log, "Tunnel service uninstalled"); let r = do_single_rpc_call::<_, ()>( diff --git a/cli/src/tunnels/singleton_server.rs b/cli/src/tunnels/singleton_server.rs index 703c09f5120..77cc701ca46 100644 --- a/cli/src/tunnels/singleton_server.rs +++ b/cli/src/tunnels/singleton_server.rs @@ -217,7 +217,7 @@ impl BroadcastLogSink { } } - fn get_brocaster(&self) -> broadcast::Sender> { + pub fn get_brocaster(&self) -> broadcast::Sender> { self.tx.clone() } diff --git a/cli/src/util/errors.rs b/cli/src/util/errors.rs index 6f4630d0c54..c82e14acc8b 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -509,8 +509,14 @@ pub enum CodeError { ServerAuthRequired, #[error("challenge not yet issued")] AuthChallengeNotIssued, + #[error("challenge token is invalid")] + AuthChallengeBadToken, #[error("unauthorized client refused")] AuthMismatch, + #[error("keyring communication timed out after 5s")] + KeyringTimeout, + #[error("no host is connected to the tunnel relay")] + NoTunnelEndpoint, } makeAnyError!( diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 676e0b2f0f8..24c30e69af9 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -41,8 +41,7 @@ ".code-workspace", "language-configuration.json", "icon-theme.json", - "color-theme.json", - ".code-snippets" + "color-theme.json" ], "filenames": [ "settings.json", @@ -54,10 +53,6 @@ "profiles.json", "devcontainer.json", ".devcontainer.json" - ], - "filenamePatterns": [ - "**/User/snippets/*.json", - "**/User/profiles/*/snippets/*.json" ] }, { "id": "json", diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index 0f365702269..3a5459401f9 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -30,5 +30,16 @@ "start": "^\\s*#pragma\\s+region\\b", "end": "^\\s*#pragma\\s+endregion\\b" } - } + }, + "onEnterRules": [ + { + // Decrease indentation after single line if/else if/else, for, or while + "previousLineText": "^\\s*(((else ?)?if|for|while)\\s*\\(.*\\)\\s*|else\\s*)$", + // But make sure line doesn't have braces or is not another if statement + "beforeText": "^\\s+([^{i\\s]|i(?!f\\b))", + "action": { + "indent": "outdent" + } + } + ] } diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index f5abb0738f5..962188ea971 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "2918bd69cfcc658bd5b4ec1b106bb008e5c21120" + "commitHash": "772323937fedd65c6dc1c8ce6ea41d97415ed7d1" } }, "license": "MIT", diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index d50cac7ae3c..c08bea9ce01 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dotnet/csharp-tmLanguage/commit/2918bd69cfcc658bd5b4ec1b106bb008e5c21120", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/772323937fedd65c6dc1c8ce6ea41d97415ed7d1", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -286,6 +286,9 @@ { "include": "#nameof-expression" }, + { + "include": "#default-literal-expression" + }, { "include": "#throw-expression" }, @@ -298,6 +301,9 @@ { "include": "#verbatim-interpolated-string" }, + { + "include": "#type-builtin" + }, { "include": "#this-or-base-expression" }, @@ -381,13 +387,19 @@ "using-directive": { "patterns": [ { - "begin": "\\b(using)\\b\\s+(static)\\s+", + "begin": "(\\b(global)\\b\\s+)?\\b(using)\\b\\s+(static)\\b\\s+(\\b(unsafe)\\b\\s+)?", "beginCaptures": { - "1": { + "2": { + "name": "keyword.other.global.cs" + }, + "3": { "name": "keyword.other.using.cs" }, - "2": { + "4": { "name": "keyword.other.static.cs" + }, + "6": { + "name": "storage.modifier.cs" } }, "end": "(?=;)", @@ -398,12 +410,18 @@ ] }, { - "begin": "\\b(using)\\s+(?=(@?[_[:alpha:]][_[:alnum:]]*)\\s*=)", + "begin": "(\\b(global)\\b\\s+)?\\b(using)\\b\\s+(\\b(unsafe)\\b\\s+)?(?=(@?[_[:alpha:]][_[:alnum:]]*)\\s*=)", "beginCaptures": { - "1": { + "2": { + "name": "keyword.other.global.cs" + }, + "3": { "name": "keyword.other.using.cs" }, - "2": { + "5": { + "name": "storage.modifier.cs" + }, + "6": { "name": "entity.name.type.alias.cs" } }, @@ -421,9 +439,12 @@ ] }, { - "begin": "\\b(using)\\s*(?!\\(|\\s|var)", + "begin": "(\\b(global)\\b\\s+)?\\b(using)\\s*(?!\\(|\\s|var)", "beginCaptures": { - "1": { + "2": { + "name": "keyword.other.global.cs" + }, + "3": { "name": "keyword.other.using.cs" } }, @@ -574,7 +595,7 @@ }, "storage-modifier": { "name": "storage.modifier.cs", - "match": "(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s*\n(?=\\()", + "begin": "(?x)\n(new)(?:\\s+\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n))?\\s*\n(?=\\()", "beginCaptures": { "1": { "name": "keyword.other.new.cs" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index f8ab6ac49a4..d39fbd0e22f 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -34,9 +34,9 @@ minimatch@^5.1.0: brace-expansion "^2.0.1" semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" diff --git a/extensions/css/cgmanifest.json b/extensions/css/cgmanifest.json index e64ba7e11ae..fe46b5c4acd 100644 --- a/extensions/css/cgmanifest.json +++ b/extensions/css/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "microsoft/vscode-css", "repositoryUrl": "https://github.com/microsoft/vscode-css", - "commitHash": "1452547185a1793c946cf67f8c7c9001716e32c3" + "commitHash": "3bd00206f6b0d16eb2eba53fb886462eb8c58baa" } }, "licenseDetail": [ diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index c50611017c2..c44db9bd68f 100644 --- a/extensions/css/syntaxes/css.tmLanguage.json +++ b/extensions/css/syntaxes/css.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-css/commit/1452547185a1793c946cf67f8c7c9001716e32c3", + "version": "https://github.com/microsoft/vscode-css/commit/3bd00206f6b0d16eb2eba53fb886462eb8c58baa", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -850,7 +850,7 @@ ] }, { - "begin": "(?i)(? { - if (session && session.configuration.serverReadyAction) { + context.subscriptions.push(vscode.debug.onDidStartDebugSession(session => { + if (session.configuration.serverReadyAction) { const detector = ServerReadyDetector.start(session); if (detector) { ServerReadyDetector.startListeningTerminalData(); diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index 3f18a1ffc5d..17ccacfc94a 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -47,8 +47,6 @@ const invokeCompletionContext: CompletionContext = { }; suite('Tests for Expand Abbreviations (HTML)', () => { - const oldValueForExcludeLanguages = workspace.getConfiguration('emmet').inspect('excludeLanguages'); - const oldValueForIncludeLanguages = workspace.getConfiguration('emmet').inspect('includeLanguages'); teardown(closeAllEditors); test('Expand snippets (HTML)', () => { @@ -364,6 +362,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); test('Expand html when inside script tag with javascript type if js is mapped to html (HTML)', async () => { + const oldConfig = workspace.getConfiguration('emmet').inspect('includeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global); await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(24, 10, 24, 10); @@ -374,12 +373,13 @@ suite('Tests for Expand Abbreviations (HTML)', () => { await expandPromise; assert.strictEqual(editor.document.getText(), htmlContents.replace('span.bye', '')); }); - return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForIncludeLanguages || {}, ConfigurationTarget.Global); + await workspace.getConfiguration('emmet').update('includeLanguages', oldConfig, ConfigurationTarget.Global); }); test('Expand html in completion list when inside script tag with javascript type if js is mapped to html (HTML)', async () => { const abbreviation = 'span.bye'; const expandedText = ''; + const oldConfig = workspace.getConfiguration('emmet').inspect('includeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global); await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(24, 10, 24, 10); @@ -399,7 +399,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); return Promise.resolve(); }); - return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForIncludeLanguages || {}, ConfigurationTarget.Global); + await workspace.getConfiguration('emmet').update('includeLanguages', oldConfig, ConfigurationTarget.Global); }); // test('No expanding when html is excluded in the settings', () => { @@ -411,9 +411,10 @@ suite('Tests for Expand Abbreviations (HTML)', () => { // }); test('No expanding when html is excluded in the settings in completion list', async () => { + const oldConfig = workspace.getConfiguration('emmet').inspect('excludeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('excludeLanguages', ['html'], ConfigurationTarget.Global); await testHtmlCompletionProvider(new Selection(9, 6, 9, 6), '', '', true); - return workspace.getConfiguration('emmet').update('excludeLanguages', oldValueForExcludeLanguages ? oldValueForExcludeLanguages.globalValue : undefined, ConfigurationTarget.Global); + await workspace.getConfiguration('emmet').update('excludeLanguages', oldConfig, ConfigurationTarget.Global); }); // test('No expanding when php (mapped syntax) is excluded in the settings', () => { diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index 30868b0b969..b81869f111a 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "71b1ead8c99715f6c994115ebab7ee4a14cf0c59" + "commitHash": "8740e610a367c5e3f15be716acc7207655ced4cf" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 9cb2fc14012..39c34a97b25 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/71b1ead8c99715f6c994115ebab7ee4a14cf0c59", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/8740e610a367c5e3f15be716acc7207655ced4cf", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -677,7 +677,7 @@ }, { "match": "(?!with|get|set\\b)\\b([\\w0-9'`^._]+)", - "comments": "Here we need the \\w modifier in order to check that the words are allowed", + "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -1658,7 +1658,7 @@ }, { "match": "([\\w0-9'`^._]+)", - "comments": "Here we need the \\w modifier in order to check that the words are allowed", + "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -1832,4 +1832,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/git/package.json b/extensions/git/package.json index c75c165c503..f45447de7f1 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -254,6 +254,20 @@ "icon": "$(check)", "enablement": "!operationInProgress" }, + { + "command": "git.commitAmend", + "title": "%command.commitAmend%", + "category": "Git", + "icon": "$(check)", + "enablement": "!operationInProgress" + }, + { + "command": "git.commitSigned", + "title": "%command.commitSigned%", + "category": "Git", + "icon": "$(check)", + "enablement": "!operationInProgress" + }, { "command": "git.commitStaged", "title": "%command.commitStaged%", @@ -321,6 +335,18 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.commitAmendNoVerify", + "title": "%command.commitAmendNoVerify%", + "category": "Git", + "enablement": "!operationInProgress" + }, + { + "command": "git.commitSignedNoVerify", + "title": "%command.commitSignedNoVerify%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.commitStagedAmendNoVerify", "title": "%command.commitStagedAmendNoVerify%", @@ -873,6 +899,14 @@ "command": "git.commit", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.commitAmend", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitSigned", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.commitStaged", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -921,6 +955,14 @@ "command": "git.commitStagedSignedNoVerify", "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" }, + { + "command": "git.commitAmendNoVerify", + "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitSignedNoVerify", + "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" + }, { "command": "git.commitStagedAmendNoVerify", "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" @@ -1772,53 +1814,71 @@ }, { "command": "git.commitNoVerify", - "group": "1_commit@6", + "group": "2_commit_noverify@1", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitStagedNoVerify", - "group": "1_commit@7", + "group": "2_commit_noverify@2", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitAllNoVerify", - "group": "1_commit@8", + "group": "2_commit_noverify@3", "when": "config.git.allowNoVerifyCommit" }, + { + "command": "git.commitAmend", + "group": "3_amend@1" + }, { "command": "git.commitStagedAmend", - "group": "2_amend@1" + "group": "3_amend@2" }, { "command": "git.commitAllAmend", - "group": "2_amend@2" + "group": "3_amend@3" + }, + { + "command": "git.commitAmendNoVerify", + "group": "4_amend_noverify@1", + "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitStagedAmendNoVerify", - "group": "2_amend@3", + "group": "4_amend_noverify@2", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitAllAmendNoVerify", - "group": "2_amend@4", + "group": "4_amend_noverify@3", "when": "config.git.allowNoVerifyCommit" }, + { + "command": "git.commitSigned", + "group": "5_signoff@1" + }, { "command": "git.commitStagedSigned", - "group": "3_signoff@1" + "group": "5_signoff@2" }, { "command": "git.commitAllSigned", - "group": "3_signoff@2" + "group": "5_signoff@3" + }, + { + "command": "git.commitSignedNoVerify", + "group": "6_signoff_noverify@1", + "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitStagedSignedNoVerify", - "group": "3_signoff@3", + "group": "6_signoff_noverify@2", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitAllSignedNoVerify", - "group": "3_signoff@4", + "group": "6_signoff_noverify@3", "when": "config.git.allowNoVerifyCommit" } ], @@ -1892,31 +1952,31 @@ "git.branch": [ { "command": "git.merge", - "group": "branch@1" + "group": "1_merge@1" }, { "command": "git.rebase", - "group": "branch@2" + "group": "1_merge@2" }, { "command": "git.branch", - "group": "branch@3" + "group": "2_branch@1" }, { "command": "git.branchFrom", - "group": "branch@4" + "group": "2_branch@2" }, { "command": "git.renameBranch", - "group": "branch@5" + "group": "3_modify@1" }, { "command": "git.deleteBranch", - "group": "branch@6" + "group": "3_modify@2" }, { "command": "git.publish", - "group": "branch@7" + "group": "4_publish@1" } ], "git.remotes": [ @@ -1932,40 +1992,40 @@ "git.stash": [ { "command": "git.stash", - "group": "stash@1" + "group": "1_stash@1" }, { "command": "git.stashIncludeUntracked", - "group": "stash@2" + "group": "1_stash@2" }, { "command": "git.stashStaged", "when": "gitVersion2.35", - "group": "stash@3" + "group": "1_stash@3" }, { "command": "git.stashApplyLatest", - "group": "stash@4" + "group": "2_apply@1" }, { "command": "git.stashApply", - "group": "stash@5" + "group": "2_apply@2" }, { "command": "git.stashPopLatest", - "group": "stash@6" + "group": "3_pop@1" }, { "command": "git.stashPop", - "group": "stash@7" + "group": "3_pop@2" }, { "command": "git.stashDrop", - "group": "stash@8" + "group": "4_drop@1" }, { "command": "git.stashDropAll", - "group": "stash@9" + "group": "4_drop@2" } ], "git.tags": [ diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 74386ba1464..24f03414ce6 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -33,6 +33,8 @@ "command.cleanAllUntracked": "Discard All Untracked Changes", "command.closeAllDiffEditors": "Close All Diff Editors", "command.commit": "Commit", + "command.commitAmend": "Commit (Amend)", + "command.commitSigned": "Commit (Signed Off)", "command.commitStaged": "Commit Staged", "command.commitEmpty": "Commit Empty", "command.commitStagedSigned": "Commit Staged (Signed Off)", @@ -44,6 +46,8 @@ "command.commitStagedNoVerify": "Commit Staged (No Verify)", "command.commitEmptyNoVerify": "Commit Empty (No Verify)", "command.commitStagedSignedNoVerify": "Commit Staged (Signed Off, No Verify)", + "command.commitAmendNoVerify": "Commit (Amend, No Verify)", + "command.commitSignedNoVerify": "Commit (Signed Off, No Verify)", "command.commitStagedAmendNoVerify": "Commit Staged (Amend, No Verify)", "command.commitAllNoVerify": "Commit All (No Verify)", "command.commitAllSignedNoVerify": "Commit All (Signed Off, No Verify)", diff --git a/extensions/git/resources/icons/dark/status-type-changed.svg b/extensions/git/resources/icons/dark/status-type-changed.svg new file mode 100644 index 00000000000..ae504ae1881 --- /dev/null +++ b/extensions/git/resources/icons/dark/status-type-changed.svg @@ -0,0 +1,6 @@ + + + + T + + diff --git a/extensions/git/resources/icons/light/status-type-changed.svg b/extensions/git/resources/icons/light/status-type-changed.svg new file mode 100644 index 00000000000..ae504ae1881 --- /dev/null +++ b/extensions/git/resources/icons/light/status-type-changed.svg @@ -0,0 +1,6 @@ + + + + T + + diff --git a/extensions/git/src/actionButton.ts b/extensions/git/src/actionButton.ts index e0524d9dff6..6b99a2a967a 100644 --- a/extensions/git/src/actionButton.ts +++ b/extensions/git/src/actionButton.ts @@ -126,7 +126,7 @@ export class ActionButtonCommand { const commandGroups: Command[][] = []; for (const commands of this.postCommitCommandCenter.getSecondaryCommands()) { commandGroups.push(commands.map(c => { - return { command: 'git.commit', title: c.title, tooltip: c.tooltip, arguments: c.arguments }; + return { command: c.command, title: c.title, tooltip: c.tooltip, arguments: c.arguments }; })); } diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 375d07c4198..afa4ad1bcaf 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -363,6 +363,7 @@ function getStatus(status: Status): string { case Status.IGNORED: return 'IGNORED'; case Status.INTENT_TO_ADD: return 'INTENT_TO_ADD'; case Status.INTENT_TO_RENAME: return 'INTENT_TO_RENAME'; + case Status.TYPE_CHANGED: return 'TYPE_CHANGED'; case Status.ADDED_BY_US: return 'ADDED_BY_US'; case Status.ADDED_BY_THEM: return 'ADDED_BY_THEM'; case Status.DELETED_BY_US: return 'DELETED_BY_US'; diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 30b8c271103..ae1d57d3098 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -79,6 +79,7 @@ export const enum Status { IGNORED, INTENT_TO_ADD, INTENT_TO_RENAME, + TYPE_CHANGED, ADDED_BY_US, ADDED_BY_THEM, diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 4213aafa495..8eba9cbac03 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -750,15 +750,22 @@ export class CommandCenter { if (uri !== undefined) { // Launch desktop client if currently in web + let target = `${env.uriScheme}://vscode.git/clone?url=${encodeURIComponent(uri)}`; if (env.uiKind === UIKind.Web) { - let target = `${env.uriScheme}://vscode.git/clone?url=${encodeURIComponent(uri)}`; if (ref !== undefined) { target += `&ref=${encodeURIComponent(ref)}`; } return Uri.parse(target); } - // If already in desktop client, directly clone + // If already in desktop client but in a remote window, we need to force a new window + // so that the git extension can access the local filesystem for cloning + if (env.remoteName !== undefined) { + target += `&windowId=_blank`; + return Uri.parse(target); + } + + // Otherwise, directly clone void this.clone(uri, undefined, { ref: ref }); } } @@ -1966,6 +1973,16 @@ export class CommandCenter { await this.commitWithAnyInput(repository, { postCommitCommand }); } + @command('git.commitAmend', { repository: true }) + async commitAmend(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { amend: true }); + } + + @command('git.commitSigned', { repository: true }) + async commitSigned(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { signoff: true }); + } + @command('git.commitStaged', { repository: true }) async commitStaged(repository: Repository): Promise { await this.commitWithAnyInput(repository, { all: false }); @@ -2084,6 +2101,16 @@ export class CommandCenter { await this.commitWithAnyInput(repository, { all: false, signoff: true, noVerify: true }); } + @command('git.commitAmendNoVerify', { repository: true }) + async commitAmendNoVerify(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { amend: true, noVerify: true }); + } + + @command('git.commitSignedNoVerify', { repository: true }) + async commitSignedNoVerify(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { signoff: true, noVerify: true }); + } + @command('git.commitStagedAmendNoVerify', { repository: true }) async commitStagedAmendNoVerify(repository: Repository): Promise { await this.commitWithAnyInput(repository, { all: false, amend: true, noVerify: true }); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index d0412aec7f7..ab03a354d11 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,7 +12,7 @@ import * as which from 'which'; import { EventEmitter } from 'events'; import * as iconv from '@vscode/iconv-lite-umd'; import * as filetype from 'file-type'; -import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh } from './util'; +import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant } from './util'; import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; @@ -477,18 +477,18 @@ export class Git { return folderPath; } - async getRepositoryRoot(repositoryPath: string): Promise { - const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel']); + async getRepositoryRoot(pathInsidePossibleRepository: string): Promise { + const result = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--show-toplevel']); // Keep trailing spaces which are part of the directory name - const repoPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); + const repositoryRootPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); if (isWindows) { // On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped // drive path back, you get the UNC path for the mapped drive. So we will try to normalize it back to the // mapped drive path, if possible - const repoUri = Uri.file(repoPath); - const pathUri = Uri.file(repositoryPath); + const repoUri = Uri.file(repositoryRootPath); + const pathUri = Uri.file(pathInsidePossibleRepository); if (repoUri.authority.length !== 0 && pathUri.authority.length === 0) { // eslint-disable-next-line local/code-no-look-behind-regex const match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path); @@ -520,7 +520,18 @@ export class Git { } } - return repoPath; + // Handle symbolic links + // Git 2.31 added the `--path-format` flag to rev-parse which + // allows us to get the relative path of the repository root + if (!pathEquals(pathInsidePossibleRepository, repositoryRootPath) && + !isDescendant(repositoryRootPath, pathInsidePossibleRepository) && + !isDescendant(pathInsidePossibleRepository, repositoryRootPath) && + this.compareGitVersionTo('2.31.0') !== -1) { + const relativePathResult = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--path-format=relative', '--show-toplevel',]); + return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimLeft().replace(/[\r\n]+$/, '')); + } + + return repositoryRootPath; } async getRepositoryDotGit(repositoryPath: string): Promise<{ path: string; commonPath?: string }> { diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 0775f15855b..eacfca8f035 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -493,8 +493,9 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu @sequentialize async openRepository(repoPath: string, openIfClosed = false): Promise { this.logger.trace(`Opening repository: ${repoPath}`); - if (this.getRepositoryExact(repoPath)) { - this.logger.trace(`Repository for path ${repoPath} already exists`); + const existingRepository = await this.getRepositoryExact(repoPath); + if (existingRepository) { + this.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`); return; } @@ -524,8 +525,9 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath); this.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`); - if (this.getRepositoryExact(repositoryRoot)) { - this.logger.trace(`Repository for path ${repositoryRoot} already exists`); + const existingRepository = await this.getRepositoryExact(repositoryRoot); + if (existingRepository) { + this.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); return; } @@ -763,10 +765,16 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu return liveRepository && liveRepository.repository; } - private getRepositoryExact(repoPath: string): Repository | undefined { - const openRepository = this.openRepositories - .find(r => pathEquals(r.repository.root, repoPath)); - return openRepository?.repository; + private async getRepositoryExact(repoPath: string): Promise { + const repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); + + for (const openRepository of this.openRepositories) { + const rootPathCanonical = await fs.promises.realpath(openRepository.repository.root, { encoding: 'utf8' }); + if (pathEquals(rootPathCanonical, repoPathCanonical)) { + return openRepository.repository; + } + } + return undefined; } private getOpenRepository(repository: Repository): OpenRepository | undefined; @@ -931,12 +939,14 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu return true; } - const result = await Promise.all(workspaceFolders.map(async folder => { - const workspaceFolderRealPath = await this.getWorkspaceFolderRealPath(folder); - return workspaceFolderRealPath ? pathEquals(workspaceFolderRealPath, repositoryPath) || isDescendant(workspaceFolderRealPath, repositoryPath) : undefined; - })); + // The repository path may be a canonical path or it may contain a symbolic link so we have + // to match it against the workspace folders and the canonical paths of the workspace folders + const workspaceFolderPaths = new Set([ + ...workspaceFolders.map(folder => folder.uri.fsPath), + ...await Promise.all(workspaceFolders.map(folder => this.getWorkspaceFolderRealPath(folder))) + ]); - return !result.some(r => r); + return !Array.from(workspaceFolderPaths).some(folder => folder && (pathEquals(folder, repositoryPath) || isDescendant(folder, repositoryPath))); } private async getWorkspaceFolderRealPath(workspaceFolder: WorkspaceFolder): Promise { diff --git a/extensions/git/src/postCommitCommands.ts b/extensions/git/src/postCommitCommands.ts index 1def1716a80..d4e227b6db7 100644 --- a/extensions/git/src/postCommitCommands.ts +++ b/extensions/git/src/postCommitCommands.ts @@ -117,7 +117,7 @@ export class CommitCommandsCenter { const commandFromStorage = allCommands.find(c => c.arguments?.length === 2 && c.arguments[1] === this.getPostCommitCommandStringFromStorage()); const commandFromSetting = allCommands.find(c => c.arguments?.length === 2 && c.arguments[1] === this.getPostCommitCommandStringFromSetting()); - return commandFromStorage ?? commandFromSetting ?? this.getCommitCommand(); + return commandFromStorage ?? commandFromSetting ?? this.getCommitCommands()[0]; } getSecondaryCommands(): Command[][] { @@ -131,7 +131,7 @@ export class CommitCommandsCenter { } if (commandGroups.length > 0) { - commandGroups[0].splice(0, 0, this.getCommitCommand()); + commandGroups.splice(0, 0, this.getCommitCommands()); } return commandGroups; @@ -169,7 +169,7 @@ export class CommitCommandsCenter { return `postCommitCommand:${this.repository.root}`; } - private getCommitCommand(): Command { + private getCommitCommands(): Command[] { const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); // Branch protection @@ -196,7 +196,10 @@ export class CommitCommandsCenter { l10n.t('Committing Changes to New Branch...'); } - return { command: 'git.commit', title: l10n.t('{0} Commit', icon ?? '$(check)'), tooltip, arguments: [this.repository.sourceControl, null] }; + return [ + { command: 'git.commit', title: l10n.t('{0} Commit', icon ?? '$(check)'), tooltip, arguments: [this.repository.sourceControl, null] }, + { command: 'git.commitAmend', title: l10n.t('{0} Commit (Amend)', icon ?? '$(check)'), tooltip, arguments: [this.repository.sourceControl, null] }, + ]; } private getPostCommitCommandStringFromSetting(): string | undefined { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 1593e7309be..77739e11ce9 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -59,6 +59,7 @@ export class Resource implements SourceControlResourceState { case Status.IGNORED: return l10n.t('Ignored'); case Status.INTENT_TO_ADD: return l10n.t('Intent to Add'); case Status.INTENT_TO_RENAME: return l10n.t('Intent to Rename'); + case Status.TYPE_CHANGED: return l10n.t('Type Changed'); case Status.BOTH_DELETED: return l10n.t('Conflict: Both Deleted'); case Status.ADDED_BY_US: return l10n.t('Conflict: Added By Us'); case Status.DELETED_BY_THEM: return l10n.t('Conflict: Deleted By Them'); @@ -112,6 +113,7 @@ export class Resource implements SourceControlResourceState { Untracked: getIconUri('status-untracked', 'light'), Ignored: getIconUri('status-ignored', 'light'), Conflict: getIconUri('status-conflict', 'light'), + TypeChanged: getIconUri('status-type-changed', 'light') }, dark: { Modified: getIconUri('status-modified', 'dark'), @@ -121,7 +123,8 @@ export class Resource implements SourceControlResourceState { Copied: getIconUri('status-copied', 'dark'), Untracked: getIconUri('status-untracked', 'dark'), Ignored: getIconUri('status-ignored', 'dark'), - Conflict: getIconUri('status-conflict', 'dark') + Conflict: getIconUri('status-conflict', 'dark'), + TypeChanged: getIconUri('status-type-changed', 'dark') } }; @@ -138,6 +141,7 @@ export class Resource implements SourceControlResourceState { case Status.IGNORED: return Resource.Icons[theme].Ignored; case Status.INTENT_TO_ADD: return Resource.Icons[theme].Added; case Status.INTENT_TO_RENAME: return Resource.Icons[theme].Renamed; + case Status.TYPE_CHANGED: return Resource.Icons[theme].TypeChanged; case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict; case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict; case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict; @@ -197,6 +201,8 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_RENAMED: case Status.INTENT_TO_RENAME: return 'R'; + case Status.TYPE_CHANGED: + return 'T'; case Status.UNTRACKED: return 'U'; case Status.IGNORED: @@ -223,6 +229,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_MODIFIED: return new ThemeColor('gitDecoration.stageModifiedResourceForeground'); case Status.MODIFIED: + case Status.TYPE_CHANGED: return new ThemeColor('gitDecoration.modifiedResourceForeground'); case Status.INDEX_DELETED: return new ThemeColor('gitDecoration.stageDeletedResourceForeground'); @@ -257,6 +264,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_MODIFIED: case Status.MODIFIED: case Status.INDEX_COPIED: + case Status.TYPE_CHANGED: return 2; case Status.IGNORED: return 3; @@ -525,6 +533,7 @@ class ResourceCommandResolver { case Status.INDEX_RENAMED: case Status.INDEX_ADDED: case Status.INTENT_TO_RENAME: + case Status.TYPE_CHANGED: return toGitUri(resource.original, 'HEAD'); case Status.MODIFIED: @@ -560,7 +569,8 @@ class ResourceCommandResolver { case Status.UNTRACKED: case Status.IGNORED: case Status.INTENT_TO_ADD: - case Status.INTENT_TO_RENAME: { + case Status.INTENT_TO_RENAME: + case Status.TYPE_CHANGED: { const uriString = resource.resourceUri.toString(); const [indexStatus] = this.repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); @@ -609,6 +619,9 @@ class ResourceCommandResolver { case Status.INTENT_TO_RENAME: return l10n.t('{0} (Intent to add)', basename); + case Status.TYPE_CHANGED: + return l10n.t('{0} (Type changed)', basename); + default: return ''; } @@ -2188,6 +2201,7 @@ export class Repository implements Disposable { case 'D': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; case 'A': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; case 'R': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_RENAME, useIcons, renameUri)); break; + case 'T': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.TYPE_CHANGED, useIcons, renameUri)); break; } return undefined; diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index b11a72fd3c2..1a928aec322 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -430,9 +430,9 @@ safe-buffer@~5.2.0: integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" diff --git a/extensions/github-authentication/extension-browser.webpack.config.js b/extensions/github-authentication/extension-browser.webpack.config.js index 37b207eb056..f109e203569 100644 --- a/extensions/github-authentication/extension-browser.webpack.config.js +++ b/extensions/github-authentication/extension-browser.webpack.config.js @@ -21,7 +21,8 @@ module.exports = withBrowserDefaults({ 'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js'), './node/authServer': path.resolve(__dirname, 'src/browser/authServer'), './node/crypto': path.resolve(__dirname, 'src/browser/crypto'), - './node/fetch': path.resolve(__dirname, 'src/browser/fetch') + './node/fetch': path.resolve(__dirname, 'src/browser/fetch'), + './node/buffer': path.resolve(__dirname, 'src/browser/buffer'), } } }); diff --git a/extensions/github-authentication/src/browser/buffer.ts b/extensions/github-authentication/src/browser/buffer.ts new file mode 100644 index 00000000000..7192f5f104a --- /dev/null +++ b/extensions/github-authentication/src/browser/buffer.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function base64Encode(text: string): string { + return btoa(text); +} diff --git a/extensions/github-authentication/src/common/env.ts b/extensions/github-authentication/src/common/env.ts index 7b99a148373..ebc474936aa 100644 --- a/extensions/github-authentication/src/common/env.ts +++ b/extensions/github-authentication/src/common/env.ts @@ -29,6 +29,10 @@ export function isSupportedClient(uri: Uri): boolean { export function isSupportedTarget(type: AuthProviderType, gheUri?: Uri): boolean { return ( type === AuthProviderType.github || - /\.ghe\.com$/.test(gheUri!.authority) + isHostedGitHubEnterprise(gheUri!) ); } + +export function isHostedGitHubEnterprise(uri: Uri): boolean { + return /\.ghe\.com$/.test(uri.authority); +} diff --git a/extensions/github-authentication/src/common/errors.ts b/extensions/github-authentication/src/common/errors.ts new file mode 100644 index 00000000000..3ba3dfc006a --- /dev/null +++ b/extensions/github-authentication/src/common/errors.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const TIMED_OUT_ERROR = 'Timed out'; + +// These error messages are internal and should not be shown to the user in any way. +export const USER_CANCELLATION_ERROR = 'User Cancelled'; +export const NETWORK_ERROR = 'network error'; diff --git a/extensions/github-authentication/src/common/logger.ts b/extensions/github-authentication/src/common/logger.ts index 84225bd707f..cf90c4176a9 100644 --- a/extensions/github-authentication/src/common/logger.ts +++ b/extensions/github-authentication/src/common/logger.ts @@ -26,4 +26,7 @@ export class Log { this.output.error(message); } + public warn(message: string): void { + this.output.warn(message); + } } diff --git a/extensions/github-authentication/src/config.ts b/extensions/github-authentication/src/config.ts new file mode 100644 index 00000000000..30b9dd66265 --- /dev/null +++ b/extensions/github-authentication/src/config.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IConfig { + // The client ID of the GitHub OAuth app + gitHubClientId: string; + gitHubClientSecret?: string; +} + +// For easy access to mixin client ID and secret +export const Config: IConfig = { + gitHubClientId: '01ab8ac9400c4e429b23' +}; diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts new file mode 100644 index 00000000000..5bc9d095385 --- /dev/null +++ b/extensions/github-authentication/src/flows.ts @@ -0,0 +1,500 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import { ProgressLocation, Uri, commands, env, l10n, window } from 'vscode'; +import { Log } from './common/logger'; +import { Config } from './config'; +import { UriEventHandler } from './github'; +import { fetching } from './node/fetch'; +import { LoopbackAuthServer } from './node/authServer'; +import { promiseFromEvent } from './common/utils'; +import { isHostedGitHubEnterprise } from './common/env'; +import { NETWORK_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; + +interface IGitHubDeviceCodeResponse { + device_code: string; + user_code: string; + verification_uri: string; + interval: number; +} + +interface IFlowOptions { + // GitHub.com + readonly supportsGitHubDotCom: boolean; + // A GitHub Enterprise Server that is hosted by an organization + readonly supportsGitHubEnterpriseServer: boolean; + // A GitHub Enterprise Server that is hosted by GitHub for an organization + readonly supportsHostedGitHubEnterprise: boolean; + + // Runtimes - there are constraints on which runtimes support which flows + readonly supportsWebWorkerExtensionHost: boolean; + readonly supportsRemoteExtensionHost: boolean; + + // Clients - see `isSupportedClient` in `common/env.ts` for what constitutes a supported client + readonly supportsSupportedClients: boolean; + readonly supportsUnsupportedClients: boolean; + + // Configurations - some flows require a client secret + readonly supportsNoClientSecret: boolean; +} + +export const enum GitHubTarget { + DotCom, + Enterprise, + HostedEnterprise +} + +export const enum ExtensionHost { + WebWorker, + Remote, + Local +} + +interface IFlowQuery { + target: GitHubTarget; + extensionHost: ExtensionHost; + isSupportedClient: boolean; +} + +interface IFlowTriggerOptions { + scopes: string; + baseUri: Uri; + logger: Log; + redirectUri: Uri; + nonce: string; + callbackUri: Uri; + uriHandler: UriEventHandler; + enterpriseUri?: Uri; +} + +interface IFlow { + label: string; + options: IFlowOptions; + trigger(options: IFlowTriggerOptions): Promise; +} + +async function exchangeCodeForToken( + logger: Log, + endpointUri: Uri, + redirectUri: Uri, + code: string, + enterpriseUri?: Uri +): Promise { + logger.info('Exchanging code for token...'); + + const clientSecret = Config.gitHubClientSecret; + if (!clientSecret) { + throw new Error('No client secret configured for GitHub authentication.'); + } + + const body = new URLSearchParams([ + ['code', code], + ['client_id', Config.gitHubClientId], + ['redirect_uri', redirectUri.toString(true)], + ['client_secret', clientSecret] + ]); + if (enterpriseUri) { + body.append('github_enterprise', enterpriseUri.toString(true)); + } + const result = await fetching(endpointUri.toString(true), { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': body.toString() + + }, + body: body.toString() + }); + + if (result.ok) { + const json = await result.json(); + logger.info('Token exchange success!'); + return json.access_token; + } else { + const text = await result.text(); + const error = new Error(text); + error.name = 'GitHubTokenExchangeError'; + throw error; + } +} + +const allFlows: IFlow[] = [ + new class UrlHandlerFlow implements IFlow { + label = l10n.t('url handler'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + // Supporting GHES would be challenging because different versions + // used a different client ID. We could try to detect the version + // and use the right one, but that's a lot of work when we have + // other flows that work well. + supportsGitHubEnterpriseServer: false, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: true, + // exchanging a code for a token requires a client secret + supportsNoClientSecret: false, + supportsSupportedClients: true, + supportsUnsupportedClients: false + }; + + async trigger({ + scopes, + baseUri, + redirectUri, + logger, + nonce, + callbackUri, + uriHandler, + enterpriseUri + }: IFlowTriggerOptions): Promise { + logger.info(`Trying without local server... (${scopes})`); + return await window.withProgress({ + location: ProgressLocation.Notification, + title: l10n.t({ + message: 'Signing in to {0}...', + args: [baseUri.authority], + comment: ['The {0} will be a url, e.g. github.com'] + }), + cancellable: true + }, async (_, token) => { + const promise = uriHandler.waitForCode(logger, scopes, nonce, token); + + const searchParams = new URLSearchParams([ + ['client_id', Config.gitHubClientId], + ['redirect_uri', redirectUri.toString(true)], + ['scope', scopes], + ['state', encodeURIComponent(callbackUri.toString(true))] + ]); + + // The extra toString, parse is apparently needed for env.openExternal + // to open the correct URL. + const uri = Uri.parse(baseUri.with({ + path: '/login/oauth/authorize', + query: searchParams.toString() + }).toString(true)); + await env.openExternal(uri); + + const code = await promise; + + const proxyEndpoints: { [providerId: string]: string } | undefined = await commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); + const endpointUrl = proxyEndpoints?.github + ? Uri.parse(`${proxyEndpoints.github}login/oauth/access_token`) + : baseUri.with({ path: '/login/oauth/access_token' }); + + const accessToken = await exchangeCodeForToken(logger, endpointUrl, redirectUri, code, enterpriseUri); + return accessToken; + }); + } + }, + new class LocalServerFlow implements IFlow { + label = l10n.t('local server'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + // Supporting GHES would be challenging because different versions + // used a different client ID. We could try to detect the version + // and use the right one, but that's a lot of work when we have + // other flows that work well. + supportsGitHubEnterpriseServer: false, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + // Web worker can't open a port to listen for the redirect + supportsWebWorkerExtensionHost: false, + // exchanging a code for a token requires a client secret + supportsNoClientSecret: false, + supportsSupportedClients: true, + supportsUnsupportedClients: true + }; + async trigger({ + scopes, + baseUri, + redirectUri, + logger, + enterpriseUri + }: IFlowTriggerOptions): Promise { + logger.info(`Trying with local server... (${scopes})`); + return await window.withProgress({ + location: ProgressLocation.Notification, + title: l10n.t({ + message: 'Signing in to {0}...', + args: [baseUri.authority], + comment: ['The {0} will be a url, e.g. github.com'] + }), + cancellable: true + }, async (_, token) => { + const searchParams = new URLSearchParams([ + ['client_id', Config.gitHubClientId], + ['redirect_uri', redirectUri.toString(true)], + ['scope', scopes], + ]); + + const loginUrl = baseUri.with({ + path: '/login/oauth/authorize', + query: searchParams.toString() + }); + const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl.toString(true)); + const port = await server.start(); + + let codeToExchange; + try { + env.openExternal(Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); + const { code } = await Promise.race([ + server.waitForOAuthResponse(), + new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout + promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise + ]); + codeToExchange = code; + } finally { + setTimeout(() => { + void server.stop(); + }, 5000); + } + + const accessToken = await exchangeCodeForToken( + logger, + baseUri.with({ path: '/login/oauth/access_token' }), + redirectUri, + codeToExchange, + enterpriseUri); + return accessToken; + }); + } + }, + new class DeviceCodeFlow implements IFlow { + label = l10n.t('device code'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + supportsGitHubEnterpriseServer: true, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + // CORS prevents this from working in web workers + supportsWebWorkerExtensionHost: false, + supportsNoClientSecret: true, + supportsSupportedClients: true, + supportsUnsupportedClients: true + }; + async trigger({ scopes, baseUri, logger }: IFlowTriggerOptions) { + logger.info(`Trying device code flow... (${scopes})`); + + // Get initial device code + const uri = baseUri.with({ + path: '/login/device/code', + query: `client_id=${Config.gitHubClientId}&scope=${scopes}` + }); + const result = await fetching(uri.toString(true), { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + if (!result.ok) { + throw new Error(`Failed to get one-time code: ${await result.text()}`); + } + + const json = await result.json() as IGitHubDeviceCodeResponse; + + const button = l10n.t('Copy & Continue to GitHub'); + const modalResult = await window.showInformationMessage( + l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }), + { + modal: true, + detail: l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.') + }, button); + + if (modalResult !== button) { + throw new Error(USER_CANCELLATION_ERROR); + } + + await env.clipboard.writeText(json.user_code); + + const uriToOpen = await env.asExternalUri(Uri.parse(json.verification_uri)); + await env.openExternal(uriToOpen); + + return await this.waitForDeviceCodeAccessToken(baseUri, json); + } + + private async waitForDeviceCodeAccessToken( + baseUri: Uri, + json: IGitHubDeviceCodeResponse, + ): Promise { + return await window.withProgress({ + location: ProgressLocation.Notification, + cancellable: true, + title: l10n.t({ + message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}', + args: [json.verification_uri, json.user_code], + comment: [ + 'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456', + '{Locked="[{0}]({0})"}' + ] + }) + }, async (_, token) => { + const refreshTokenUri = baseUri.with({ + path: '/login/oauth/access_token', + query: `client_id=${Config.gitHubClientId}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code` + }); + + // Try for 2 minutes + const attempts = 120 / json.interval; + for (let i = 0; i < attempts; i++) { + await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); + if (token.isCancellationRequested) { + throw new Error(USER_CANCELLATION_ERROR); + } + let accessTokenResult; + try { + accessTokenResult = await fetching(refreshTokenUri.toString(true), { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + } catch { + continue; + } + + if (!accessTokenResult.ok) { + continue; + } + + const accessTokenJson = await accessTokenResult.json(); + + if (accessTokenJson.error === 'authorization_pending') { + continue; + } + + if (accessTokenJson.error) { + throw new Error(accessTokenJson.error_description); + } + + return accessTokenJson.access_token; + } + + throw new Error(TIMED_OUT_ERROR); + }); + } + }, + new class PatFlow implements IFlow { + label = l10n.t('personal access token'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + supportsGitHubEnterpriseServer: true, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: true, + supportsNoClientSecret: true, + // PATs can't be used with Settings Sync so we don't enable this flow + // for supported clients + supportsSupportedClients: false, + supportsUnsupportedClients: true + }; + + async trigger({ scopes, baseUri, logger, enterpriseUri }: IFlowTriggerOptions) { + logger.info(`Trying to retrieve PAT... (${scopes})`); + + const button = l10n.t('Continue to GitHub'); + const modalResult = await window.showInformationMessage( + l10n.t('Continue to GitHub to create a Personal Access Token (PAT)'), + { + modal: true, + detail: l10n.t('To finish authenticating, navigate to GitHub to create a PAT then paste the PAT into the input box.') + }, button); + + if (modalResult !== button) { + throw new Error(USER_CANCELLATION_ERROR); + } + + const description = `${env.appName} (${scopes})`; + const uriToOpen = await env.asExternalUri(baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` })); + await env.openExternal(uriToOpen); + const token = await window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true }); + if (!token) { throw new Error(USER_CANCELLATION_ERROR); } + + const appUri = !enterpriseUri || isHostedGitHubEnterprise(enterpriseUri) + ? Uri.parse(`${baseUri.scheme}://api.${baseUri.authority}`) + : Uri.parse(`${baseUri.scheme}://${baseUri.authority}/api/v3`); + + const tokenScopes = await this.getScopes(token, appUri, logger); // Example: ['repo', 'user'] + const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' + if (!scopesList.every(scope => { + const included = tokenScopes.includes(scope); + if (included || !scope.includes(':')) { + return included; + } + + return scope.split(':').some(splitScopes => { + return tokenScopes.includes(splitScopes); + }); + })) { + throw new Error(`The provided token does not match the requested scopes: ${scopes}`); + } + + return token; + } + + private async getScopes(token: string, serverUri: Uri, logger: Log): Promise { + try { + logger.info('Getting token scopes...'); + const result = await fetching(serverUri.toString(), { + headers: { + Authorization: `token ${token}`, + 'User-Agent': `${env.appName} (${env.appHost})` + } + }); + + if (result.ok) { + const scopes = result.headers.get('X-OAuth-Scopes'); + return scopes ? scopes.split(',').map(scope => scope.trim()) : []; + } else { + logger.error(`Getting scopes failed: ${result.statusText}`); + throw new Error(result.statusText); + } + } catch (ex) { + logger.error(ex.message); + throw new Error(NETWORK_ERROR); + } + } + } +]; + +export function getFlows(query: IFlowQuery) { + return allFlows.filter(flow => { + let useFlow: boolean = true; + switch (query.target) { + case GitHubTarget.DotCom: + useFlow &&= flow.options.supportsGitHubDotCom; + break; + case GitHubTarget.Enterprise: + useFlow &&= flow.options.supportsGitHubEnterpriseServer; + break; + case GitHubTarget.HostedEnterprise: + useFlow &&= flow.options.supportsHostedGitHubEnterprise; + break; + } + + switch (query.extensionHost) { + case ExtensionHost.Remote: + useFlow &&= flow.options.supportsRemoteExtensionHost; + break; + case ExtensionHost.WebWorker: + useFlow &&= flow.options.supportsWebWorkerExtensionHost; + break; + } + + if (!Config.gitHubClientSecret) { + useFlow &&= flow.options.supportsNoClientSecret; + } + + if (query.isSupportedClient) { + // TODO: revisit how we support PAT in GHES but not DotCom... but this works for now since + // there isn't another flow that has supportsSupportedClients = false + useFlow &&= (flow.options.supportsSupportedClients || query.target !== GitHubTarget.DotCom); + } else { + useFlow &&= flow.options.supportsUnsupportedClients; + } + return useFlow; + }); +} diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 0588065eacd..71aa17bd5cc 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -7,10 +7,11 @@ import * as vscode from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import { Keychain } from './common/keychain'; import { GitHubServer, IGitHubServer } from './githubServer'; -import { arrayEquals } from './common/utils'; +import { PromiseAdapter, arrayEquals, promiseFromEvent } from './common/utils'; import { ExperimentationTelemetry } from './common/experimentationService'; import { Log } from './common/logger'; import { crypto } from './node/crypto'; +import { TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; interface SessionData { id: string; @@ -29,9 +30,63 @@ export enum AuthProviderType { } export class UriEventHandler extends vscode.EventEmitter implements vscode.UriHandler { + private readonly _pendingNonces = new Map(); + private readonly _codeExchangePromises = new Map; cancel: vscode.EventEmitter }>(); + public handleUri(uri: vscode.Uri) { this.fire(uri); } + + public async waitForCode(logger: Log, scopes: string, nonce: string, token: vscode.CancellationToken) { + const existingNonces = this._pendingNonces.get(scopes) || []; + this._pendingNonces.set(scopes, [...existingNonces, nonce]); + + let codeExchangePromise = this._codeExchangePromises.get(scopes); + if (!codeExchangePromise) { + codeExchangePromise = promiseFromEvent(this.event, this.handleEvent(logger, scopes)); + this._codeExchangePromises.set(scopes, codeExchangePromise); + } + + try { + return await Promise.race([ + codeExchangePromise.promise, + new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout + promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise + ]); + } finally { + this._pendingNonces.delete(scopes); + codeExchangePromise?.cancel.fire(); + this._codeExchangePromises.delete(scopes); + } + } + + private handleEvent: (logger: Log, scopes: string) => PromiseAdapter = + (logger: Log, scopes) => (uri, resolve, reject) => { + const query = new URLSearchParams(uri.query); + const code = query.get('code'); + const nonce = query.get('nonce'); + if (!code) { + reject(new Error('No code')); + return; + } + if (!nonce) { + reject(new Error('No nonce')); + return; + } + + const acceptedNonces = this._pendingNonces.get(scopes) || []; + if (!acceptedNonces.includes(nonce)) { + // A common scenario of this happening is if you: + // 1. Trigger a sign in with one set of scopes + // 2. Before finishing 1, you trigger a sign in with a different set of scopes + // In this scenario we should just return and wait for the next UriHandler event + // to run as we are probably still waiting on the user to hit 'Continue' + logger.info('Nonce not found in accepted nonces. Skipping this execution...'); + return; + } + + resolve(code); + }; } export class GitHubAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable { @@ -308,6 +363,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid sessions.splice(sessionIndex, 1); await this.storeSessions(sessions); + await this._githubServer.logout(session); this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] }); } else { diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 98bb1dd822f..0729c4c5077 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -4,72 +4,36 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as path from 'path'; -import { PromiseAdapter, promiseFromEvent } from './common/utils'; import { ExperimentationTelemetry } from './common/experimentationService'; import { AuthProviderType, UriEventHandler } from './github'; import { Log } from './common/logger'; import { isSupportedClient, isSupportedTarget } from './common/env'; -import { LoopbackAuthServer } from './node/authServer'; import { crypto } from './node/crypto'; import { fetching } from './node/fetch'; - -const CLIENT_ID = '01ab8ac9400c4e429b23'; -const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token'; +import { ExtensionHost, GitHubTarget, getFlows } from './flows'; +import { NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; +import { Config } from './config'; +import { base64Encode } from './node/buffer'; // This is the error message that we throw if the login was cancelled for any reason. Extensions // calling `getSession` can handle this error to know that the user cancelled the login. const CANCELLATION_ERROR = 'Cancelled'; -// These error messages are internal and should not be shown to the user in any way. -const TIMED_OUT_ERROR = 'Timed out'; -const USER_CANCELLATION_ERROR = 'User Cancelled'; -const NETWORK_ERROR = 'network error'; const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect'; const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect'; export interface IGitHubServer { login(scopes: string): Promise; + logout(session: vscode.AuthenticationSession): Promise; getUserInfo(token: string): Promise<{ id: string; accountName: string }>; sendAdditionalTelemetryInfo(session: vscode.AuthenticationSession): Promise; friendlyName: string; } -interface IGitHubDeviceCodeResponse { - device_code: string; - user_code: string; - verification_uri: string; - interval: number; -} - -async function getScopes(token: string, serverUri: vscode.Uri, logger: Log): Promise { - try { - logger.info('Getting token scopes...'); - const result = await fetching(serverUri.toString(), { - headers: { - Authorization: `token ${token}`, - 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` - } - }); - - if (result.ok) { - const scopes = result.headers.get('X-OAuth-Scopes'); - return scopes ? scopes.split(',').map(scope => scope.trim()) : []; - } else { - logger.error(`Getting scopes failed: ${result.statusText}`); - throw new Error(result.statusText); - } - } catch (ex) { - logger.error(ex.message); - throw new Error(NETWORK_ERROR); - } -} export class GitHubServer implements IGitHubServer { readonly friendlyName: string; - private readonly _pendingNonces = new Map(); - private readonly _codeExchangePromises = new Map; cancel: vscode.EventEmitter }>(); private readonly _type: AuthProviderType; private _redirectEndpoint: string | undefined; @@ -117,9 +81,14 @@ export class GitHubServer implements IGitHubServer { } // TODO@joaomoreno TODO@TylerLeonhardt + private _isNoCorsEnvironment: boolean | undefined; private async isNoCorsEnvironment(): Promise { + if (this._isNoCorsEnvironment !== undefined) { + return this._isNoCorsEnvironment; + } const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/dummy`)); - return (uri.scheme === 'https' && /^((insiders\.)?vscode|github)\./.test(uri.authority)) || (uri.scheme === 'http' && /^localhost/.test(uri.authority)); + this._isNoCorsEnvironment = (uri.scheme === 'https' && /^((insiders\.)?vscode|github)\./.test(uri.authority)) || (uri.scheme === 'http' && /^localhost/.test(uri.authority)); + return this._isNoCorsEnvironment; } public async login(scopes: string): Promise { @@ -148,48 +117,33 @@ export class GitHubServer implements IGitHubServer { const supportedClient = isSupportedClient(callbackUri); const supportedTarget = isSupportedTarget(this._type, this._ghesUri); - if (supportedClient && supportedTarget) { - try { - return await this.doLoginWithoutLocalServer(scopes, nonce, callbackUri); - } catch (e) { - this._logger.error(e); - userCancelled = e.message ?? e === USER_CANCELLATION_ERROR; - } - } - // Starting a local server is only supported if: - // 1. We are in a UI extension because we need to open a port on the machine that has the browser - // 2. We are in a node runtime because we need to open a port on the machine - // 3. code exchange can only be done with a supported target - if ( - this._extensionKind === vscode.ExtensionKind.UI && - typeof navigator === 'undefined' && - supportedTarget - ) { - try { - await promptToContinue(vscode.l10n.t('local server')); - return await this.doLoginWithLocalServer(scopes); - } catch (e) { - userCancelled = this.processLoginError(e); - } - } + const flows = getFlows({ + target: this._type === AuthProviderType.github + ? GitHubTarget.DotCom + : supportedTarget ? GitHubTarget.HostedEnterprise : GitHubTarget.Enterprise, + extensionHost: typeof navigator === 'undefined' + ? this._extensionKind === vscode.ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote + : ExtensionHost.WebWorker, + isSupportedClient: supportedClient + }); - // We only can use the Device Code flow when we have a full node environment because of CORS. - if (typeof navigator === 'undefined') { - try { - await promptToContinue(vscode.l10n.t('device code')); - return await this.doLoginDeviceCodeFlow(scopes); - } catch (e) { - userCancelled = this.processLoginError(e); - } - } - // In a supported environment, we can't use PAT auth because we use this auth for Settings Sync and it doesn't support PATs. - // With that said, GitHub Enterprise isn't used by Settings Sync so we can use PATs for that. - if (!supportedClient || this._type === AuthProviderType.githubEnterprise) { + for (const flow of flows) { try { - await promptToContinue(vscode.l10n.t('personal access token')); - return await this.doLoginWithPat(scopes); + if (flow !== flows[0]) { + await promptToContinue(flow.label); + } + return await flow.trigger({ + scopes, + callbackUri, + nonce, + baseUri: this.baseUri, + logger: this._logger, + uriHandler: this._uriHandler, + enterpriseUri: this._ghesUri, + redirectUri: vscode.Uri.parse(await this.getRedirectEndpoint()), + }); } catch (e) { userCancelled = this.processLoginError(e); } @@ -198,298 +152,55 @@ export class GitHubServer implements IGitHubServer { throw new Error(userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.'); } - private async doLoginWithoutLocalServer(scopes: string, nonce: string, callbackUri: vscode.Uri): Promise { - this._logger.info(`Trying without local server... (${scopes})`); - return await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: vscode.l10n.t({ - message: 'Signing in to {0}...', - args: [this.baseUri.authority], - comment: ['The {0} will be a url, e.g. github.com'] - }), - cancellable: true - }, async (_, token) => { - const existingNonces = this._pendingNonces.get(scopes) || []; - this._pendingNonces.set(scopes, [...existingNonces, nonce]); - const redirectUri = await this.getRedirectEndpoint(); - const searchParams = new URLSearchParams([ - ['client_id', CLIENT_ID], - ['redirect_uri', redirectUri], - ['scope', scopes], - ['state', encodeURIComponent(callbackUri.toString(true))] - ]); + public async logout(session: vscode.AuthenticationSession): Promise { + this._logger.trace(`Deleting session (${session.id}) from server...`); - const uri = vscode.Uri.parse(this.baseUri.with({ - path: '/login/oauth/authorize', - query: searchParams.toString() - }).toString(true)); - await vscode.env.openExternal(uri); + if (!Config.gitHubClientSecret) { + this._logger.warn('No client secret configured for GitHub authentication. The token has been deleted with best effort on this system, but we are unable to delete the token on server without the client secret.'); + return; + } - // Register a single listener for the URI callback, in case the user starts the login process multiple times - // before completing it. - let codeExchangePromise = this._codeExchangePromises.get(scopes); - if (!codeExchangePromise) { - codeExchangePromise = promiseFromEvent(this._uriHandler!.event, this.handleUri(scopes)); - this._codeExchangePromises.set(scopes, codeExchangePromise); + // Only attempt to delete OAuth tokens. They are always prefixed with `gho_`. + // https://docs.github.com/en/rest/apps/oauth-applications#about-oauth-apps-and-oauth-authorizations-of-github-apps + if (!session.accessToken.startsWith('gho_')) { + this._logger.warn('The token being deleted is not an OAuth token. It has been deleted locally, but we cannot delete it on server.'); + return; + } + + if (!isSupportedTarget(this._type, this._ghesUri)) { + this._logger.trace('GitHub.com and GitHub hosted GitHub Enterprise are the only options that support deleting tokens on the server. Skipping.'); + return; + } + + const authHeader = 'Basic ' + base64Encode(`${Config.gitHubClientId}:${Config.gitHubClientSecret}`); + const uri = this.getServerUri(`/applications/${Config.gitHubClientId}/token`); + + try { + // Defined here: https://docs.github.com/en/rest/apps/oauth-applications?apiVersion=2022-11-28#delete-an-app-token + const result = await fetching(uri.toString(true), { + method: 'DELETE', + headers: { + Accept: 'application/vnd.github+json', + Authorization: authHeader, + 'X-GitHub-Api-Version': '2022-11-28', + 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` + }, + body: JSON.stringify({ access_token: session.accessToken }), + }); + + if (result.status === 204) { + this._logger.trace(`Successfully deleted token from session (${session.id}) from server.`); + return; } try { - return await Promise.race([ - codeExchangePromise.promise, - new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout - promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise - ]); - } finally { - this._pendingNonces.delete(scopes); - codeExchangePromise?.cancel.fire(); - this._codeExchangePromises.delete(scopes); + const body = await result.text(); + throw new Error(body); + } catch (e) { + throw new Error(`${result.status} ${result.statusText}`); } - }); - } - - private async doLoginWithLocalServer(scopes: string): Promise { - this._logger.info(`Trying with local server... (${scopes})`); - return await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: vscode.l10n.t({ - message: 'Signing in to {0}...', - args: [this.baseUri.authority], - comment: ['The {0} will be a url, e.g. github.com'] - }), - cancellable: true - }, async (_, token) => { - const redirectUri = await this.getRedirectEndpoint(); - const searchParams = new URLSearchParams([ - ['client_id', CLIENT_ID], - ['redirect_uri', redirectUri], - ['scope', scopes], - ]); - - const loginUrl = this.baseUri.with({ - path: '/login/oauth/authorize', - query: searchParams.toString() - }); - const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl.toString(true)); - const port = await server.start(); - - let codeToExchange; - try { - vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); - const { code } = await Promise.race([ - server.waitForOAuthResponse(), - new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout - promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise - ]); - codeToExchange = code; - } finally { - setTimeout(() => { - void server.stop(); - }, 5000); - } - - const accessToken = await this.exchangeCodeForToken(codeToExchange); - return accessToken; - }); - } - - private async doLoginDeviceCodeFlow(scopes: string): Promise { - this._logger.info(`Trying device code flow... (${scopes})`); - - // Get initial device code - const uri = this.baseUri.with({ - path: '/login/device/code', - query: `client_id=${CLIENT_ID}&scope=${scopes}` - }); - const result = await fetching(uri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - if (!result.ok) { - throw new Error(`Failed to get one-time code: ${await result.text()}`); - } - - const json = await result.json() as IGitHubDeviceCodeResponse; - - const button = vscode.l10n.t('Copy & Continue to GitHub'); - const modalResult = await vscode.window.showInformationMessage( - vscode.l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }), - { - modal: true, - detail: vscode.l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.') - }, button); - - if (modalResult !== button) { - throw new Error(USER_CANCELLATION_ERROR); - } - - await vscode.env.clipboard.writeText(json.user_code); - - const uriToOpen = await vscode.env.asExternalUri(vscode.Uri.parse(json.verification_uri)); - await vscode.env.openExternal(uriToOpen); - - return await this.waitForDeviceCodeAccessToken(json); - } - - private async doLoginWithPat(scopes: string): Promise { - this._logger.info(`Trying to retrieve PAT... (${scopes})`); - - const button = vscode.l10n.t('Continue to GitHub'); - const modalResult = await vscode.window.showInformationMessage( - vscode.l10n.t('Continue to GitHub to create a Personal Access Token (PAT)'), - { - modal: true, - detail: vscode.l10n.t('To finish authenticating, navigate to GitHub to create a PAT then paste the PAT into the input box.') - }, button); - - if (modalResult !== button) { - throw new Error(USER_CANCELLATION_ERROR); - } - - const description = `${vscode.env.appName} (${scopes})`; - const uriToOpen = await vscode.env.asExternalUri(this.baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` })); - await vscode.env.openExternal(uriToOpen); - const token = await vscode.window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true }); - if (!token) { throw new Error(USER_CANCELLATION_ERROR); } - - const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user'] - const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' - if (!scopesList.every(scope => { - const included = tokenScopes.includes(scope); - if (included || !scope.includes(':')) { - return included; - } - - return scope.split(':').some(splitScopes => { - return tokenScopes.includes(splitScopes); - }); - })) { - throw new Error(`The provided token does not match the requested scopes: ${scopes}`); - } - - return token; - } - - private async waitForDeviceCodeAccessToken( - json: IGitHubDeviceCodeResponse, - ): Promise { - return await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - cancellable: true, - title: vscode.l10n.t({ - message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}', - args: [json.verification_uri, json.user_code], - comment: [ - 'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456', - '{Locked="[{0}]({0})"}' - ] - }) - }, async (_, token) => { - const refreshTokenUri = this.baseUri.with({ - path: '/login/oauth/access_token', - query: `client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code` - }); - - // Try for 2 minutes - const attempts = 120 / json.interval; - for (let i = 0; i < attempts; i++) { - await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); - if (token.isCancellationRequested) { - throw new Error(USER_CANCELLATION_ERROR); - } - let accessTokenResult; - try { - accessTokenResult = await fetching(refreshTokenUri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - } catch { - continue; - } - - if (!accessTokenResult.ok) { - continue; - } - - const accessTokenJson = await accessTokenResult.json(); - - if (accessTokenJson.error === 'authorization_pending') { - continue; - } - - if (accessTokenJson.error) { - throw new Error(accessTokenJson.error_description); - } - - return accessTokenJson.access_token; - } - - throw new Error(TIMED_OUT_ERROR); - }); - } - - private handleUri: (scopes: string) => PromiseAdapter = - (scopes) => (uri, resolve, reject) => { - const query = new URLSearchParams(uri.query); - const code = query.get('code'); - const nonce = query.get('nonce'); - if (!code) { - reject(new Error('No code')); - return; - } - if (!nonce) { - reject(new Error('No nonce')); - return; - } - - const acceptedNonces = this._pendingNonces.get(scopes) || []; - if (!acceptedNonces.includes(nonce)) { - // A common scenario of this happening is if you: - // 1. Trigger a sign in with one set of scopes - // 2. Before finishing 1, you trigger a sign in with a different set of scopes - // In this scenario we should just return and wait for the next UriHandler event - // to run as we are probably still waiting on the user to hit 'Continue' - this._logger.info('Nonce not found in accepted nonces. Skipping this execution...'); - return; - } - - resolve(this.exchangeCodeForToken(code)); - }; - - private async exchangeCodeForToken(code: string): Promise { - this._logger.info('Exchanging code for token...'); - - const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); - const endpointUrl = proxyEndpoints?.github ? `${proxyEndpoints.github}login/oauth/access_token` : GITHUB_TOKEN_URL; - - const body = new URLSearchParams([['code', code]]); - if (this._type === AuthProviderType.githubEnterprise) { - body.append('github_enterprise', this.baseUri.toString(true)); - body.append('redirect_uri', await this.getRedirectEndpoint()); - } - const result = await fetching(endpointUrl, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': body.toString() - - }, - body: body.toString() - }); - - if (result.ok) { - const json = await result.json(); - this._logger.info('Token exchange success!'); - return json.access_token; - } else { - const text = await result.text(); - const error = new Error(text); - error.name = 'GitHubTokenExchangeError'; - throw error; + } catch (e) { + this._logger.warn('Failed to delete token from server.' + e.message ?? e); } } diff --git a/extensions/github-authentication/src/node/buffer.ts b/extensions/github-authentication/src/node/buffer.ts new file mode 100644 index 00000000000..8e6208aa22a --- /dev/null +++ b/extensions/github-authentication/src/node/buffer.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function base64Encode(text: string): string { + return Buffer.from(text, 'binary').toString('base64'); +} diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 609eb719d50..752933d621f 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -364,9 +364,9 @@ node-fetch@2.6.7: whatwg-url "^5.0.0" semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" diff --git a/extensions/github/package.json b/extensions/github/package.json index da9ccd0084e..6e655fca983 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -136,7 +136,7 @@ "github.branchProtection": { "type": "boolean", "scope": "resource", - "default": false, + "default": true, "description": "%config.branchProtection%" }, "github.gitAuthentication": { diff --git a/extensions/github/src/branchProtection.ts b/extensions/github/src/branchProtection.ts index 8966b41c155..a7d18c41b9f 100644 --- a/extensions/github/src/branchProtection.ts +++ b/extensions/github/src/branchProtection.ts @@ -8,6 +8,7 @@ import { Repository as GitHubRepository, RepositoryRuleset } from '@octokit/grap import { AuthenticationError, getOctokitGraphql } from './auth'; import { API, BranchProtection, BranchProtectionProvider, BranchProtectionRule, Repository } from './typings/git'; import { DisposableStore, getRepositoryFromUrl } from './util'; +import TelemetryReporter from '@vscode/extension-telemetry'; const REPOSITORY_QUERY = ` query repositoryPermissions($owner: String!, $repo: String!) { @@ -60,7 +61,7 @@ export class GithubBranchProtectionProviderManager { if (enabled) { for (const repository of this.gitAPI.repositories) { - this.providerDisposables.add(this.gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger))); + this.providerDisposables.add(this.gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); } } else { this.providerDisposables.dispose(); @@ -72,10 +73,11 @@ export class GithubBranchProtectionProviderManager { constructor( private readonly gitAPI: API, private readonly globalState: Memento, - private readonly logger: LogOutputChannel) { + private readonly logger: LogOutputChannel, + private readonly telemetryReporter: TelemetryReporter) { this.disposables.add(this.gitAPI.onDidOpenRepository(repository => { if (this._enabled) { - this.providerDisposables.add(gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger))); + this.providerDisposables.add(gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); } })); @@ -110,7 +112,8 @@ export class GithubBranchProtectionProvider implements BranchProtectionProvider constructor( private readonly repository: Repository, private readonly globalState: Memento, - private readonly logger: LogOutputChannel) { + private readonly logger: LogOutputChannel, + private readonly telemetryReporter: TelemetryReporter) { // Restore branch protection from global state this.branchProtection = this.globalState.get(this.globalStateKey, []); @@ -199,17 +202,27 @@ export class GithubBranchProtectionProvider implements BranchProtectionProvider // Save branch protection to global state await this.globalState.update(this.globalStateKey, branchProtection); this.logger.trace(`Branch protection for "${this.repository.rootUri.toString()}": ${JSON.stringify(branchProtection)}.`); + + /* __GDPR__ + "branchProtection" : { + "owner": "lszomoru", + "rulesetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of repository rulesets" } + } + */ + this.telemetryReporter.sendTelemetryEvent('branchProtection', undefined, { rulesetCount: this.branchProtection.length }); } catch (err) { this.logger.warn(`Failed to update repository branch protection: ${err.message}`); if (err instanceof AuthenticationError) { // A GitHub authentication session could be missing if the user has not yet - // signed in with their GitHub account or they have signed out. In this case - // we have to clear the branch protection information. - this.branchProtection = branchProtection; - this._onDidChangeBranchProtection.fire(this.repository.rootUri); + // signed in with their GitHub account or they have signed out. If there is + // branch protection information we have to clear it. + if (this.branchProtection.length !== 0) { + this.branchProtection = branchProtection; + this._onDidChangeBranchProtection.fire(this.repository.rootUri); - await this.globalState.update(this.globalStateKey, undefined); + await this.globalState.update(this.globalStateKey, undefined); + } } } } diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts index e6a91970c61..1827768312e 100644 --- a/extensions/github/src/extension.ts +++ b/extensions/github/src/extension.ts @@ -97,7 +97,7 @@ function initializeGitExtension(context: ExtensionContext, telemetryReporter: Te disposables.add(registerCommands(gitAPI)); disposables.add(new GithubCredentialProviderManager(gitAPI)); - disposables.add(new GithubBranchProtectionProviderManager(gitAPI, context.globalState, logger)); + disposables.add(new GithubBranchProtectionProviderManager(gitAPI, context.globalState, logger, telemetryReporter)); disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler(telemetryReporter))); disposables.add(gitAPI.registerRemoteSourcePublisher(new GithubRemoteSourcePublisher(gitAPI))); disposables.add(new GitHubCanonicalUriProvider(gitAPI)); diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts index bd8cdfc5c82..911f0e5376b 100644 --- a/extensions/github/src/links.ts +++ b/extensions/github/src/links.ts @@ -191,11 +191,13 @@ export function getVscodeDevHost(): string { } export async function ensurePublished(repository: Repository, file: vscode.Uri) { + await repository.status(); + if ((repository.state.HEAD?.type === RefType.Head || repository.state.HEAD?.type === RefType.Tag) // If HEAD is not published, make sure it is && !repository?.state.HEAD?.upstream ) { - const publishBranch = vscode.l10n.t('Publish Branch'); + const publishBranch = vscode.l10n.t('Publish Branch & Copy Link'); const selection = await vscode.window.showInformationMessage( vscode.l10n.t('The current branch is not published to the remote. Would you like to publish your branch before copying a link?'), { modal: true }, @@ -209,7 +211,7 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) } const uncommittedChanges = [...repository.state.workingTreeChanges, ...repository.state.indexChanges]; - if (uncommittedChanges.find((c) => c.uri.toString() === file.toString())) { + if (uncommittedChanges.find((c) => c.uri.toString() === file.toString()) && !repository.state.HEAD?.ahead && !repository.state.HEAD?.behind) { const commitChanges = vscode.l10n.t('Commit Changes'); const copyAnyway = vscode.l10n.t('Copy Anyway'); const selection = await vscode.window.showWarningMessage( @@ -225,7 +227,7 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) throw new vscode.CancellationError(); } } else if (repository.state.HEAD?.ahead) { - const pushCommits = vscode.l10n.t('Push Commits'); + const pushCommits = vscode.l10n.t('Push Commits & Copy Link'); const selection = await vscode.window.showInformationMessage( vscode.l10n.t('The current branch has unpublished commits. Would you like to push your commits before copying a link?'), { modal: true }, @@ -236,6 +238,18 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) } await repository.push(); + } else if (repository.state.HEAD?.behind) { + const pull = vscode.l10n.t('Pull Changes & Copy Link'); + const selection = await vscode.window.showInformationMessage( + vscode.l10n.t('The current branch is not up to date. Would you like to pull before copying a link?'), + { modal: true }, + pull + ); + if (selection !== pull) { + throw new vscode.CancellationError(); + } + + await repository.pull(); } await repository.status(); diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts index 4b4acd66879..7ac67937a47 100644 --- a/extensions/github/src/typings/git.d.ts +++ b/extensions/github/src/typings/git.d.ts @@ -79,6 +79,7 @@ export const enum Status { IGNORED, INTENT_TO_ADD, INTENT_TO_RENAME, + TYPE_CHANGED, ADDED_BY_US, ADDED_BY_THEM, diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock index c35f7727b52..323df7dadbe 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -493,9 +493,9 @@ once@^1.4.0: wrappy "1" semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" diff --git a/extensions/html-language-features/server/src/test/folding.test.ts b/extensions/html-language-features/server/src/test/folding.test.ts index 44aaea9026c..ec33f7a5198 100644 --- a/extensions/html-language-features/server/src/test/folding.test.ts +++ b/extensions/html-language-features/server/src/test/folding.test.ts @@ -37,7 +37,7 @@ function r(startLine: number, endLine: number, kind?: string): ExpectedIndentRan return { startLine, endLine, kind }; } -suite('HTML Folding', async () => { +suite('HTML Folding', () => { test('Embedded JavaScript', async () => { const input = [ diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 9b13e63e639..c6dda3cbc53 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -349,14 +349,14 @@ ms@2.1.2: integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" diff --git a/extensions/ipynb/src/notebookAttachmentCleaner.ts b/extensions/ipynb/src/notebookAttachmentCleaner.ts index 8468b0cffb2..cad19f07b29 100644 --- a/extensions/ipynb/src/notebookAttachmentCleaner.ts +++ b/extensions/ipynb/src/notebookAttachmentCleaner.ts @@ -232,7 +232,7 @@ export class AttachmentCleaner implements vscode.CodeActionProvider { if (cell.index > -1 && !objectEquals(markdownAttachmentsInUse, cell.metadata.attachments)) { const updateMetadata: { [key: string]: any } = deepClone(cell.metadata); if (Object.keys(markdownAttachmentsInUse).length === 0) { - updateMetadata.attachments = null; + updateMetadata.attachments = undefined; } else { updateMetadata.attachments = markdownAttachmentsInUse; } diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json index a4db862ab7d..be9e7439fe7 100644 --- a/extensions/java/cgmanifest.json +++ b/extensions/java/cgmanifest.json @@ -4,13 +4,47 @@ "component": { "type": "git", "git": { - "name": "atom/language-java", - "repositoryUrl": "https://github.com/atom/language-java", - "commitHash": "29f977dc42a7e2568b39bb6fb34c4ef108eb59b3" + "name": "redhat-developer/vscode-java", + "repositoryUrl": "https://github.com/redhat-developer/vscode-java", + "commitHash": "5fb57e8e1c5d776b21be13cd7227b25b87edf4a6" } }, "license": "MIT", - "version": "0.32.1" + "licenseDetail": [ + "Copyright (c) 2014 GitHub Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of this software and associated documentation files (the", + "\"Software\"), to deal in the Software without restriction, including", + "without limitation the rights to use, copy, modify, merge, publish,", + "distribute, sublicense, and/or sell copies of the Software, and to", + "permit persons to whom the Software is furnished to do so, subject to", + "the following conditions:", + "", + "The above copyright notice and this permission notice shall be", + "included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "", + "--------------------------------------------------------------------", + "", + "This package was derived from a TextMate bundle located at", + "https://github.com/textmate/java.tmbundle and distributed under the following", + "license, located in `README.mdown`:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose." + ], + "description": "This grammar was derived from https://github.com/atom/language-java/blob/master/grammars/java.cson.", + "version": "1.21.0" } ], "version": 1 diff --git a/extensions/java/package.json b/extensions/java/package.json index d71aa1c146c..6788ddd2133 100644 --- a/extensions/java/package.json +++ b/extensions/java/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin atom/language-java grammars/java.cson ./syntaxes/java.tmLanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin redhat-developer/vscode-java language-support/java/java.tmLanguage.json ./syntaxes/java.tmLanguage.json" }, "contributes": { "languages": [ diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index be70cbb27c3..337545b0233 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/atom/language-java/blob/master/grammars/java.cson", + "This file has been converted from https://github.com/redhat-developer/vscode-java/blob/master/language-support/java/java.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-java/commit/29f977dc42a7e2568b39bb6fb34c4ef108eb59b3", + "version": "https://github.com/redhat-developer/vscode-java/commit/5fb57e8e1c5d776b21be13cd7227b25b87edf4a6", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -1378,6 +1378,17 @@ }, "properties": { "patterns": [ + { + "match": "(\\.)\\s*(new)", + "captures": { + "1": { + "name": "punctuation.separator.period.java" + }, + "2": { + "name": "keyword.control.new.java" + } + } + }, { "match": "(\\.)\\s*([a-zA-Z_$][\\w$]*)(?=\\s*\\.\\s*[a-zA-Z_$][\\w$]*)", "captures": { @@ -1571,6 +1582,31 @@ }, "strings": { "patterns": [ + { + "begin": "\"\"\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.java" + } + }, + "end": "\"\"\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.java" + } + }, + "name": "string.quoted.triple.java", + "patterns": [ + { + "match": "\\\\\"\"\"", + "name": "constant.character.escape.java" + }, + { + "match": "\\\\.", + "name": "constant.character.escape.java" + } + ] + }, { "begin": "\"", "beginCaptures": { @@ -1632,6 +1668,9 @@ { "match": "[a-zA-Z$_][\\.a-zA-Z0-9$_]*", "name": "storage.type.java" + }, + { + "include": "#comments" } ] }, diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index abfef6e8c4c..4029985233a 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -188,6 +188,15 @@ "action": { "indent": "indent" } + }, + { + // Decrease indentation after single line if/else if/else, for, or while + "previousLineText": "^\\s*(((else ?)?if|for|while)\\s*\\(.*\\)\\s*|else\\s*)$", + // But make sure line doesn't have braces or is not another if statement + "beforeText": "^\\s+([^{i\\s]|i(?!f\\b))", + "action": { + "indent": "outdent" + } } ] } diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 9726c5ab25a..4fe09e087aa 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/e0aefd8205cc9d1bc7859cc5babbee0d833dca0f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -134,7 +134,7 @@ "name": "keyword.control.flow.js" } }, - "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -299,7 +299,7 @@ { "name": "meta.var.expr.js", "begin": "(?=(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1808,7 +1876,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1969,7 +2037,7 @@ "name": "storage.type.namespace.js" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2006,7 +2074,7 @@ "name": "entity.name.type.alias.js" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2024,7 +2092,7 @@ "name": "keyword.control.intrinsic.js" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2038,7 +2106,7 @@ "name": "keyword.operator.assignment.js" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2229,7 +2297,7 @@ "name": "keyword.control.default.js" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2241,7 +2309,7 @@ }, { "name": "meta.export.js", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3904,7 +3972,7 @@ "name": "keyword.operator.type.annotation.js" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3918,7 +3986,7 @@ "name": "meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.js meta.return.type.arrow.js", "patterns": [ { diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index b0fb54f1707..b9869694bdd 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/e0aefd8205cc9d1bc7859cc5babbee0d833dca0f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -134,7 +134,7 @@ "name": "keyword.control.flow.js.jsx" } }, - "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -299,7 +299,7 @@ { "name": "meta.var.expr.js.jsx", "begin": "(?=(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1808,7 +1876,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1969,7 +2037,7 @@ "name": "storage.type.namespace.js.jsx" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2006,7 +2074,7 @@ "name": "entity.name.type.alias.js.jsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2024,7 +2092,7 @@ "name": "keyword.control.intrinsic.js.jsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2038,7 +2106,7 @@ "name": "keyword.operator.assignment.js.jsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2229,7 +2297,7 @@ "name": "keyword.control.default.js.jsx" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2241,7 +2309,7 @@ }, { "name": "meta.export.js.jsx", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3904,7 +3972,7 @@ "name": "keyword.operator.type.annotation.js.jsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3918,7 +3986,7 @@ "name": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx", "patterns": [ { diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index d6e1404a7ac..23410ebb814 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -8,7 +8,7 @@ export type JSONLanguageStatus = { schemas: string[] }; import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, ColorInformation, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, TextEditorOptions } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, FormattingOptions as LSPFormattingOptions, @@ -51,7 +51,14 @@ interface DocumentSortingParams { } namespace DocumentSortingRequest { - export const type: RequestType = new RequestType('json/sort'); + export interface ITextEdit { + range: { + start: { line: number; character: number }; + end: { line: number; character: number }; + }; + newText: string; + } + export const type: RequestType = new RequestType('json/sort'); } export interface ISchemaAssociations { @@ -95,6 +102,7 @@ export type JSONSchemaSettings = { export namespace SettingIds { export const enableFormatter = 'json.format.enable'; export const enableKeepLines = 'json.format.keepLines'; + export const enableSortOnSave = 'json.sortOnSave.enable'; export const enableValidation = 'json.validate.enable'; export const enableSchemaDownload = 'json.schemaDownload.enable'; export const maxItemsComputed = 'json.maxItemsComputed'; @@ -163,25 +171,23 @@ export async function startClient(context: ExtensionContext, newLanguageClient: window.showInformationMessage(l10n.t('JSON schema cache cleared.')); })); + toDispose.push(workspace.onWillSaveTextDocument(event => { + const sortOnSave = workspace.getConfiguration().get(SettingIds.enableSortOnSave); + const document = event.document; + if (sortOnSave && (document.languageId === 'json' || document.languageId === 'jsonc')) { + const documentOptions = getOptionsForDocument(document); + const textEditsPromise = getSortTextEdits(document, documentOptions?.tabSize, documentOptions?.insertSpaces); + event.waitUntil(textEditsPromise); + } + })); + toDispose.push(commands.registerCommand('json.sort', async () => { if (isClientReady) { const textEditor = window.activeTextEditor; if (textEditor) { - const document = textEditor.document; - const filesConfig = workspace.getConfiguration('files', document); - const options: SortOptions = { - tabSize: textEditor.options.tabSize ? Number(textEditor.options.tabSize) : 4, - insertSpaces: textEditor.options.insertSpaces ? Boolean(textEditor.options.insertSpaces) : true, - trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), - trimFinalNewlines: filesConfig.get('trimFinalNewlines'), - insertFinalNewline: filesConfig.get('insertFinalNewline'), - }; - const params: DocumentSortingParams = { - uri: document.uri.toString(), - options - }; - const textEdits = await client.sendRequest(DocumentSortingRequest.type, params); + const documentOptions = textEditor.options; + const textEdits = await getSortTextEdits(textEditor.document, documentOptions.tabSize, documentOptions.insertSpaces); const success = await textEditor.edit(mutator => { for (const edit of textEdits) { mutator.replace(client.protocol2CodeConverter.asRange(edit.range), edit.newText); @@ -471,6 +477,29 @@ export async function startClient(context: ExtensionContext, newLanguageClient: } } + async function getSortTextEdits(document: TextDocument, tabSize: string | number = 4, insertSpaces: string | boolean = true): Promise { + const filesConfig = workspace.getConfiguration('files', document); + const options: SortOptions = { + tabSize: Number(tabSize), + insertSpaces: Boolean(insertSpaces), + trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), + trimFinalNewlines: filesConfig.get('trimFinalNewlines'), + insertFinalNewline: filesConfig.get('insertFinalNewline'), + }; + const params: DocumentSortingParams = { + uri: document.uri.toString(), + options + }; + const edits = await client.sendRequest(DocumentSortingRequest.type, params); + // Here we convert the JSON objects to real TextEdit objects + return edits.map((edit) => { + return new TextEdit( + new Range(edit.range.start.line, edit.range.start.character, edit.range.end.line, edit.range.end.character), + edit.newText + ); + }); + } + return client; } @@ -614,3 +643,12 @@ function updateMarkdownString(h: MarkdownString): MarkdownString { function isSchemaResolveError(d: Diagnostic) { return d.code === /* SchemaResolveError */ 0x300; } + +function getOptionsForDocument(document: TextDocument): TextEditorOptions | undefined { + for (const editor of window.visibleTextEditors) { + if (editor.document.uri.toString() === document.uri.toString()) { + return editor.options; + } + } + return; +} diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 2b43041ff05..0d6b88408ef 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -91,6 +91,12 @@ "default": false, "description": "%json.format.keepLines.desc%" }, + "json.sortOnSave.enable": { + "type": "boolean", + "scope": "window", + "default": false, + "description": "%json.sortOnSave.enable.desc%" + }, "json.trace.server": { "type": "string", "scope": "window", diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 571d047802d..4091dad211a 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -8,6 +8,7 @@ "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", "json.format.keepLines.desc" : "Keep all existing new lines when formatting.", + "json.sortOnSave.enable.desc": "Enable/disable default sorting on save", "json.validate.enable.desc": "Enable/disable JSON validation.", "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 5bec70a9925..6113220bc30 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -354,14 +354,14 @@ request-light@^0.7.0: integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" diff --git a/extensions/json/build/update-grammars.js b/extensions/json/build/update-grammars.js index ff00951e126..2b7f76f8f90 100644 --- a/extensions/json/build/update-grammars.js +++ b/extensions/json/build/update-grammars.js @@ -6,16 +6,16 @@ var updateGrammar = require('vscode-grammar-updater'); -function adaptJSON(grammar, name, replacementScope) { +function adaptJSON(grammar, name, replacementScope, replaceeScope = 'json') { grammar.name = name; grammar.scopeName = `source${replacementScope}`; - + const regex = new RegExp(`\.${replaceeScope}`, 'g'); var fixScopeNames = function (rule) { if (typeof rule.name === 'string') { - rule.name = rule.name.replace(/\.json/g, replacementScope); + rule.name = rule.name.replace(regex, replacementScope); } if (typeof rule.contentName === 'string') { - rule.contentName = rule.contentName.replace(/\.json/g, replacementScope); + rule.contentName = rule.contentName.replace(regex, replacementScope); } for (var property in rule) { var value = rule[property]; @@ -35,3 +35,5 @@ var tsGrammarRepo = 'microsoft/vscode-JSON.tmLanguage'; updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSON.tmLanguage.json'); updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSONC.tmLanguage.json', grammar => adaptJSON(grammar, 'JSON with Comments', '.json.comments')); updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSONL.tmLanguage.json', grammar => adaptJSON(grammar, 'JSON Lines', '.json.lines')); + +updateGrammar.update('jeff-hykin/better-snippet-syntax', 'autogenerated/jsonc.tmLanguage.json', './syntaxes/snippets.tmLanguage.json', grammar => adaptJSON(grammar, 'Snippets', '.json.comments.snippets', 'json.comments')); diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json index 1af8426e535..fe6b0ae6e6d 100644 --- a/extensions/json/cgmanifest.json +++ b/extensions/json/cgmanifest.json @@ -11,6 +11,18 @@ }, "license": "MIT", "version": "0.0.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "jeff-hykin/better-snippet-syntax", + "repositoryUrl": "https://github.com/jeff-hykin/better-snippet-syntax", + "commitHash": "2b1bb124cb2b9c75c3c80eae1b8f3a043841d654" + } + }, + "license": "MIT", + "version": "1.0.2" } ], "version": 1 diff --git a/extensions/json/package.json b/extensions/json/package.json index 57290e9d1dd..7834eb38d75 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -31,7 +31,7 @@ ".jslintrc", ".jsonld", ".geojson", - ".ipynb", + ".ipynb", ".vuerc" ], "filenames": [ @@ -77,6 +77,20 @@ ], "filenames": [], "configuration": "./language-configuration.json" + }, + { + "id": "snippets", + "aliases": [ + "Code Snippets" + ], + "extensions": [ + ".code-snippets" + ], + "filenamePatterns": [ + "**/User/snippets/*.json", + "**/User/profiles/*/snippets/*.json" + ], + "configuration": "./language-configuration.json" } ], "grammars": [ @@ -94,6 +108,11 @@ "language": "jsonl", "scopeName": "source.json.lines", "path": "./syntaxes/JSONL.tmLanguage.json" + }, + { + "language": "snippets", + "scopeName": "source.json.comments.snippets", + "path": "./syntaxes/snippets.tmLanguage.json" } ] }, diff --git a/extensions/json/syntaxes/snippets.tmLanguage.json b/extensions/json/syntaxes/snippets.tmLanguage.json new file mode 100644 index 00000000000..289bc18f8c6 --- /dev/null +++ b/extensions/json/syntaxes/snippets.tmLanguage.json @@ -0,0 +1,7463 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/jeff-hykin/better-snippet-syntax/blob/master/autogenerated/jsonc.tmLanguage.json", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/jeff-hykin/better-snippet-syntax/commit/2b1bb124cb2b9c75c3c80eae1b8f3a043841d654", + "name": "Snippets", + "scopeName": "source.json.comments.snippets", + "patterns": [ + { + "include": "#value" + } + ], + "repository": { + "array": { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.array.begin.json.comments.snippets" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.array.end.json.comments.snippets" + } + }, + "name": "meta.structure.array.json.comments.snippets", + "patterns": [ + { + "include": "#value" + }, + { + "match": ",", + "name": "punctuation.separator.array.json.comments.snippets" + }, + { + "match": "[^\\s\\]]", + "name": "invalid.illegal.expected-array-separator.json.comments.snippets" + } + ] + }, + "basic_escape": { + "match": "\\\\(?:[\"\\\\\\/bfnrt]|(?:u[0-9a-fA-F]{4}))", + "name": "constant.character.escape.json.comments.snippets" + }, + "bnf_any": { + "match": "(?:\\}|((?:(?:(?:(?:(?:(?:((?:(\\$)([0-9]+)))|((?:(?:(\\$)(\\{))([0-9]+)(\\}))))|((?:(?:(\\$)(\\{))([0-9]+)((?:(\\/)((?:(?:(?:(?:(\\\\)(\\\\\\/))|(?:(\\\\\\\\\\\\)(\\\\\\/)))|[^\\/\\n])+))(\\/)(((?:(?:(?:(?:(?:(?:(?:(?:\\$(?:(?)*?))(\\|)(\\}))))|((?:(?:(\\$)(\\{))([0-9]+)(:)(?:(?:(?:(?:(?:\\$(?:[0-9]+))|(?:(?:\\$\\{)(?:[0-9]+)\\}))|(?:(?:\\$\\{)(?:[0-9]+)(?:\\/((?:(?:(?:(?:\\\\(?:\\\\\\/))|(?:(?:\\\\\\\\\\\\)(?:\\\\\\/)))|[^\\/\\n])+))\\/((?:(?:(?:(?:(?:(?:(?:(?:(?:\\$(?:(?)+)(\\}))))|(?:(?:(?:((?:(\\$)((?+))(\\}))))|((?:(?:(\\$)(\\{))((?)*?))(\\|)(\\}))", + "captures": { + "1": { + "name": "punctuation.section.insertion.dollar.brackets.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.brackets.json.comments.snippets" + }, + "2": { + "name": "punctuation.section.insertion.bracket.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.bracket.json.comments.snippets" + }, + "3": { + "name": "variable.other.normal.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets" + }, + "4": { + "name": "punctuation.separator.choice.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.separator.choice.json.comments.snippets" + }, + "5": { + "patterns": [ + { + "match": ",", + "name": "meta.insertion.choice.json.comments.snippets punctuation.separator.comma.json.comments.snippets" + }, + { + "include": "#choice_option" + } + ] + }, + "6": { + "name": "meta.insertion.choice.json.comments.snippets constant.other.option.json.comments.snippets" + }, + "7": { + "name": "punctuation.section.insertion.escape.escaper.json.comments.snippets comment.block.json.comments.snippets punctuation.definition.comment.insertion.escape.json.comments.snippets" + }, + "8": { + "name": "constant.character.escape.json.comments.snippets" + }, + "9": { + "name": "punctuation.section.insertion.escape.escaper.json.comments.snippets comment.block.json.comments.snippets punctuation.definition.comment.insertion.escape.json.comments.snippets" + }, + "10": { + "patterns": [ + { + "include": "#quad_backslash_match" + }, + { + "include": "#dollar_sign_escape" + }, + { + "include": "#bracket_escape" + }, + { + "include": "#basic_escape" + }, + { + "include": "#invalid_escape" + }, + { + "include": "#normal_characters" + } + ] + }, + "11": { + "patterns": [ + { + "include": "#quad_backslash_match" + }, + { + "match": "(\\\\\\\\)\\$", + "captures": { + "1": { + "name": "punctuation.section.insertion.escape.escaper.json.comments.snippets comment.block.json.comments.snippets punctuation.definition.comment.insertion.escape.json.comments.snippets" + } + }, + "name": "punctuation.section.insertion.escape.escapee.json.comments.snippets string.regexp.insertion.escape.json.comments.snippets string.quoted.double.json.comments.snippets" + }, + { + "include": "#invalid_escape" + } + ] + }, + "12": { + "patterns": [ + { + "include": "#quad_backslash_match" + }, + { + "match": "(\\\\\\\\)\\}", + "captures": { + "1": { + "name": "punctuation.section.insertion.escape.escaper.json.comments.snippets comment.block.json.comments.snippets punctuation.definition.comment.insertion.escape.json.comments.snippets" + } + }, + "name": "punctuation.section.insertion.escape.escapee.json.comments.snippets string.regexp.insertion.escape.json.comments.snippets string.quoted.double.json.comments.snippets" + } + ] + }, + "13": { + "name": "constant.character.escape.json.comments.snippets" + }, + "14": { + "name": "punctuation.separator.choice.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.separator.choice.json.comments.snippets" + }, + "15": { + "name": "punctuation.section.insertion.bracket.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.bracket.json.comments.snippets" + } + }, + "name": "meta.insertion.brackets.json.comments.snippets meta.insertion.choice.json.comments.snippets" + }, + "bnf_format": { + "match": "(?:(?:(?:(?:(?:(?:(?:(\\$)((?]+?)(>)|(\\S+?)) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?:(<)((?:\\\\[<>]|[^<>\\n])*)(>)|(\\S+?)) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { @@ -2641,47 +2641,50 @@ "5": { "name": "punctuation.definition.metadata.markdown" }, - "6": { - "name": "punctuation.definition.link.markdown" - }, "7": { - "name": "markup.underline.link.image.markdown" + "name": "punctuation.definition.link.markdown" }, "8": { - "name": "punctuation.definition.link.markdown" + "name": "markup.underline.link.image.markdown" }, "9": { - "name": "string.other.link.description.title.markdown" + "name": "punctuation.definition.link.markdown" }, "10": { - "name": "punctuation.definition.string.markdown" - }, - "11": { - "name": "punctuation.definition.string.markdown" + "name": "markup.underline.link.image.markdown" }, "12": { "name": "string.other.link.description.title.markdown" }, "13": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.begin.markdown" }, "14": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.end.markdown" }, "15": { "name": "string.other.link.description.title.markdown" }, "16": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.begin.markdown" }, "17": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.end.markdown" }, "18": { + "name": "string.other.link.description.title.markdown" + }, + "19": { + "name": "punctuation.definition.string.begin.markdown" + }, + "20": { + "name": "punctuation.definition.string.end.markdown" + }, + "21": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)((?:\\\\[<>]|[^<>\\n])*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.image.inline.markdown" }, "image-ref": { @@ -2876,7 +2879,7 @@ "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)([^<>\\n]*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n # The title \n (?:\n ((\\()[^()]*(\\))) # Match title in parens…\n | ((\")[^\"]*(\")) # or in double quotes…\n | ((')[^']*(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)((?:\\\\[<>]|[^<>\\n])*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n # The title \n (?:\n ((\\()[^()]*(\\))) # Match title in parens…\n | ((\")[^\"]*(\")) # or in double quotes…\n | ((')[^']*(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.link.inline.markdown" }, "link-ref": { diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 7e2d20a1b32..bb88d5a8bf8 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -33,7 +33,7 @@ "git": { "name": "microsoft/vscode-markdown-tm-grammar", "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", - "commitHash": "e2af2e59c84c47b7b3c3ea690d74e7001bab96a1" + "commitHash": "c635942289ebf40954e69cf3637aac906465ade8" } }, "license": "MIT", diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index c31f66d8404..dcfa85d6e93 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/e2af2e59c84c47b7b3c3ea690d74e7001bab96a1", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/c635942289ebf40954e69cf3637aac906465ade8", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -42,6 +42,9 @@ { "include": "#html" }, + { + "include": "#table" + }, { "include": "#paragraph" } @@ -2437,7 +2440,7 @@ "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" }, { - "begin": "(^|\\G)([ ]{0,3})([0-9]+\\.)([ \\t])", + "begin": "(^|\\G)([ ]{0,3})([0-9]+[\\.\\)])([ \\t])", "beginCaptures": { "3": { "name": "punctuation.definition.list.begin.markdown" @@ -2457,7 +2460,7 @@ ] }, "paragraph": { - "begin": "(^|\\G)[ ]{0,3}(?=\\S)", + "begin": "(^|\\G)[ ]{0,3}(?=[^ \\t\\n])", "name": "meta.paragraph.markdown", "patterns": [ { @@ -2470,7 +2473,7 @@ "include": "#heading-setext" } ], - "while": "(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=\\S))" + "while": "(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=[^ \\t\\n]))" }, "raw_block": { "begin": "(^|\\G)([ ]{4}|\\t)", @@ -2491,6 +2494,42 @@ ], "end": "(^|\\G)-{3}|\\.{3}\\s*$" }, + "table": { + "name": "markup.table.markdown", + "begin": "(^|\\G)(\\|)(?=[^|].+\\|\\s*$)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.table.markdown" + } + }, + "while": "(^|\\G)(?=\\|)", + "patterns": [ + { + "match": "\\|", + "name": "punctuation.definition.table.markdown" + }, + { + "match": "(?<=\\|)\\s*(:?-+:?)\\s*(?=\\|)", + "captures": { + "1": { + "name": "punctuation.separator.table.markdown" + } + } + }, + { + "match": "(?<=\\|)\\s*(?=\\S)((\\\\\\||[^|])+)(?<=\\S)\\s*(?=\\|)", + "captures": { + "1": { + "patterns": [ + { + "include": "#inline" + } + ] + } + } + } + ] + }, "inline": { "patterns": [ { diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 1f4fcbc6034..60e80b169d3 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -499,10 +499,20 @@ ] }, "markdown.editor.pasteUrlAsFormattedLink.enabled": { - "type": "boolean", + "type": "string", "scope": "resource", "markdownDescription": "%configuration.markdown.editor.pasteUrlAsFormattedLink.enabled%", - "default": true + "default":"never", + "enum": [ + "always", + "smart", + "never" + ], + "markdownEnumDescriptions": [ + "%configuration.pasteUrlAsFormattedLink.always%", + "%configuration.pasteUrlAsFormattedLink.smart%", + "%configuration.pasteUrlAsFormattedLink.never%" + ] }, "markdown.validate.enabled": { "type": "boolean", @@ -712,8 +722,8 @@ }, "dependencies": { "@vscode/extension-telemetry": "0.7.5", - "dompurify": "^2.4.1", - "highlight.js": "^11.4.0", + "dompurify": "^3.0.5", + "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.1", "morphdom": "^2.6.1", @@ -722,7 +732,7 @@ "vscode-uri": "^3.0.3" }, "devDependencies": { - "@types/dompurify": "^2.3.1", + "@types/dompurify": "^3.0.2", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "12.2.3", "@types/picomatch": "^2.3.0", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index c97971764ea..7336657621e 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -41,9 +41,12 @@ "configuration.markdown.editor.drop.copyIntoWorkspace": "Controls if files outside of the workspace that are dropped into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied dropped files should be created", "configuration.markdown.editor.filePaste.enabled": "Enable pasting files into a Markdown editor to create Markdown links. Requires enabling `#editor.pasteAs.enabled#`.", "configuration.markdown.editor.filePaste.copyIntoWorkspace": "Controls if files outside of the workspace that are pasted into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied files should be created.", - "configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls if a Markdown link is created when a URL is pasted into the Markdown editor.", "configuration.copyIntoWorkspace.mediaFiles": "Try to copy external image and video files into the workspace.", "configuration.copyIntoWorkspace.never": "Do not copy external files into the workspace.", + "configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls how a Markdown link is created when a URL is pasted into the Markdown editor. Requires enabling `#editor.pasteAs.enabled#`.", + "configuration.pasteUrlAsFormattedLink.always": "Always creates a Markdown link when a URL is pasted into the Markdown editor.", + "configuration.pasteUrlAsFormattedLink.smart": "Smartly avoids creating a Markdown link in specific cases, such as within code brackets or inside an existing Markdown link.", + "configuration.pasteUrlAsFormattedLink.never": "Never creates a Markdown link when a URL is pasted into the Markdown editor.", "configuration.markdown.validate.enabled.description": "Enable all error reporting in Markdown files.", "configuration.markdown.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, for example: `[link][ref]`. Requires enabling `#markdown.validate.enabled#`.", "configuration.markdown.validate.fragmentLinks.enabled.description": "Validate fragment links to headers in the current Markdown file, for example: `[link](#header)`. Requires enabling `#markdown.validate.enabled#`.", diff --git a/extensions/markdown-language-features/src/commands/insertResource.ts b/extensions/markdown-language-features/src/commands/insertResource.ts index 2e7b97c3048..87de74270ca 100644 --- a/extensions/markdown-language-features/src/commands/insertResource.ts +++ b/extensions/markdown-language-features/src/commands/insertResource.ts @@ -60,7 +60,7 @@ export class InsertImageFromWorkspace implements Command { } function getDefaultUri(document: vscode.TextDocument) { - const docUri = getParentDocumentUri(document); + const docUri = getParentDocumentUri(document.uri); if (docUri.scheme === Schemes.untitled) { return vscode.workspace.workspaceFolders?.[0]?.uri; } @@ -76,10 +76,10 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode await vscode.workspace.applyEdit(edit); } -function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0) { +function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false) { const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => { const selectionText = activeEditor.document.getText(selection); - const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, { + const snippet = createUriListSnippet(activeEditor.document, selectedFiles, [], title, placeholderValue, pasteAsMarkdownLink, isExternalLink, { insertAsMedia, placeholderText: selectionText, placeholderStartIndex: (i + 1) * selectedFiles.length, diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts index cb6a77e8c8d..c8f44fad2bc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts @@ -81,7 +81,7 @@ export class NewFilePathGenerator { } function getDesiredNewFilePath(config: CopyFileConfiguration, document: vscode.TextDocument, file: vscode.DataTransferFile): vscode.Uri { - const docUri = getParentDocumentUri(document); + const docUri = getParentDocumentUri(document.uri); for (const [rawGlob, rawDest] of Object.entries(config.destination)) { for (const glob of parseGlob(rawGlob)) { if (picomatch.isMatch(docUri.path, glob, { dot: true })) { diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts index 9d1bc003106..727216c02bc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { Schemes } from '../../util/schemes'; -import { createEditForMediaFiles, getMarkdownLink, mediaMimes } from './shared'; +import { createEditForMediaFiles, createEditAddingLinksForUriList, mediaMimes, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink } from './shared'; class PasteEditProvider implements vscode.DocumentPasteEditProvider { @@ -27,17 +27,18 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider { return createEdit; } - const label = vscode.l10n.t('Insert Markdown Media'); - const uriEdit = new vscode.DocumentPasteEdit('', this._id, label); + const uriEdit = new vscode.DocumentPasteEdit('', this._id, ''); const urlList = await dataTransfer.get('text/uri-list')?.asString(); if (!urlList) { return; } - const pasteEdit = await getMarkdownLink(document, ranges, urlList, token); + const pasteUrlSetting = await getPasteUrlAsFormattedLinkSetting(document); + const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); if (!pasteEdit) { return; } + uriEdit.label = pasteEdit.label; uriEdit.additionalEdit = pasteEdit.additionalEdits; uriEdit.priority = this._getPriority(dataTransfer); return uriEdit; diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts index d6946ff6fbe..7a14f6745f3 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts @@ -4,41 +4,43 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getMarkdownLink } from './shared'; - +import { createEditAddingLinksForUriList, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink, validateLink } from './shared'; class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { - private readonly _id = 'insertMarkdownLink'; + readonly id = 'insertMarkdownLink'; async provideDocumentPasteEdits( document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken, ): Promise { - const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', true); - if (!enabled) { + const pasteUrlSetting = await getPasteUrlAsFormattedLinkSetting(document); + if (pasteUrlSetting === PasteUrlAsFormattedLink.Never) { return; } - // Check if dataTransfer contains a URL const item = dataTransfer.get('text/plain'); - try { - new URL(await item?.value); - } catch (error) { + const urlList = await item?.asString(); + + if (urlList === undefined) { return; } - const label = vscode.l10n.t('Insert Markdown Link'); - const uriEdit = new vscode.DocumentPasteEdit('', this._id, label); - const urlList = await item?.asString(); + if (!validateLink(urlList).isValid) { + return; + } + + const uriEdit = new vscode.DocumentPasteEdit('', this.id, ''); if (!urlList) { return undefined; } - const pasteEdit = await getMarkdownLink(document, ranges, urlList, token); + + const pasteEdit = await createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); if (!pasteEdit) { return; } + uriEdit.label = pasteUrlSetting === PasteUrlAsFormattedLink.Smart ? vscode.l10n.t('Smartly Insert Link') : pasteEdit.label; uriEdit.additionalEdit = pasteEdit.additionalEdits; return uriEdit; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index 402ed072798..e8ba1ae49f1 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -17,6 +17,12 @@ enum MediaKind { Audio, } +export const externalUriSchemes = [ + 'http', + 'https', + 'mailto', +]; + export const mediaFileExtensions = new Map([ // Images ['bmp', MediaKind.Image], @@ -56,21 +62,66 @@ export const mediaMimes = new Set([ 'audio/x-wav', ]); -export async function getMarkdownLink(document: vscode.TextDocument, ranges: readonly vscode.Range[], urlList: string, token: vscode.CancellationToken): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> { +const smartPasteRegexes = [ + { regex: /(\[[^\[\]]*](?:\([^\(\)]*\)|\[[^\[\]]*]))/g, isMarkdownLink: false, isInline: true }, // In a Markdown link + { regex: /^```[\s\S]*?```$/gm, isMarkdownLink: false, isInline: false }, // In a backtick fenced code block + { regex: /^~~~[\s\S]*?~~~$/gm, isMarkdownLink: false, isInline: false }, // In a tildefenced code block + { regex: /^\$\$[\s\S]*?\$\$$/gm, isMarkdownLink: false, isInline: false }, // In a fenced math block + { regex: /`[^`]*`/g, isMarkdownLink: false, isInline: true }, // In inline code + { regex: /\$[^$]*\$/g, isMarkdownLink: false, isInline: true }, // In inline math +]; + +export interface SkinnyTextDocument { + offsetAt(position: vscode.Position): number; + getText(range?: vscode.Range): string; + readonly uri: vscode.Uri; +} + +export enum PasteUrlAsFormattedLink { + Always = 'always', + Smart = 'smart', + Never = 'never' +} + +export async function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument): Promise { + return vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', PasteUrlAsFormattedLink.Smart); +} + +export async function createEditAddingLinksForUriList( + document: SkinnyTextDocument, + ranges: readonly vscode.Range[], + urlList: string, + isExternalLink: boolean, + useSmartPaste: boolean, + token: vscode.CancellationToken, +): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> { + if (ranges.length === 0) { return; } - const edits: vscode.SnippetTextEdit[] = []; let placeHolderValue: number = ranges.length; let label: string = ''; - for (let i = 0; i < ranges.length; i++) { - const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue); + let pasteAsMarkdownLink: boolean = true; + + for (const range of ranges) { + const selectedRange: vscode.Range = new vscode.Range( + new vscode.Position(range.start.line, document.offsetAt(range.start)), + new vscode.Position(range.end.line, document.offsetAt(range.end)) + ); + + if (useSmartPaste) { + pasteAsMarkdownLink = checkSmartPaste(document, selectedRange, range); + } + + const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(range), placeHolderValue, pasteAsMarkdownLink, isExternalLink); if (!snippet) { return; } + + pasteAsMarkdownLink = true; placeHolderValue--; - edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet)); + edits.push(new vscode.SnippetTextEdit(range, snippet.snippet)); label = snippet.label; } @@ -80,21 +131,59 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea return { additionalEdits, label }; } -export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { +export function checkSmartPaste(document: SkinnyTextDocument, selectedRange: vscode.Range, range: vscode.Range): boolean { + if (selectedRange.isEmpty || /^[\s\n]*$/.test(document.getText(range)) || validateLink(document.getText(range)).isValid) { + return false; + } + if (/\[.*\]\(.*\)/.test(document.getText(range)) || /!\[.*\]\(.*\)/.test(document.getText(range))) { + return false; + } + for (const regex of smartPasteRegexes) { + const matches = [...document.getText().matchAll(regex.regex)]; + for (const match of matches) { + if (match.index !== undefined) { + const inLink = selectedRange.start.character > match.index && selectedRange.end.character < match.index + match[0].length; + const overLink = regex.isMarkdownLink && selectedRange.start.character === match.index && selectedRange.end.character === match.index + match[0].length; + if (inLink || overLink) { + return false; + } + } + } + } + return true; +} + +export function validateLink(urlList: string): { isValid: boolean; cleanedUrlList: string } { + let isValid = false; + let uri = undefined; + const trimmedUrlList = urlList?.trim(); //remove leading and trailing whitespace and new lines + try { + uri = vscode.Uri.parse(trimmedUrlList); + } catch (error) { + return { isValid: false, cleanedUrlList: urlList }; + } + const splitUrlList = trimmedUrlList.split(' ').filter(item => item !== ''); //split on spaces and remove empty strings + if (uri) { + isValid = splitUrlList.length === 1 && !splitUrlList[0].includes('\n') && externalUriSchemes.includes(vscode.Uri.parse(splitUrlList[0]).scheme) && !!vscode.Uri.parse(splitUrlList[0]).authority; + } + return { isValid, cleanedUrlList: splitUrlList[0] }; +} + +export async function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { if (token.isCancellationRequested) { return undefined; } - + const uriStrings: string[] = []; const uris: vscode.Uri[] = []; for (const resource of urlList.split(/\r?\n/g)) { try { uris.push(vscode.Uri.parse(resource)); + uriStrings.push(resource); } catch { // noop } } - - return createUriListSnippet(document, uris, title, placeHolderValue); + return createUriListSnippet(document, uris, uriStrings, title, placeHolderValue, pasteAsMarkdownLink, isExternalLink); } interface UriListSnippetOptions { @@ -112,27 +201,48 @@ interface UriListSnippetOptions { readonly separator?: string; } +export function appendToLinkSnippet( + snippet: vscode.SnippetString, + pasteAsMarkdownLink: boolean, + mdPath: string, + title: string, + uriString: string, + placeholderValue: number, + isExternalLink: boolean, +): vscode.SnippetString { + if (pasteAsMarkdownLink) { + snippet.appendText('['); + snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); + snippet.appendText(`](${escapeMarkdownLinkPath(isExternalLink ? uriString : mdPath, isExternalLink)})`); + } else { + snippet.appendText((escapeMarkdownLinkPath(isExternalLink ? uriString : mdPath, isExternalLink))); + } + return snippet; +} + export function createUriListSnippet( - document: vscode.TextDocument, + document: SkinnyTextDocument, uris: readonly vscode.Uri[], + uriStrings?: readonly string[], title = '', placeholderValue = 0, + pasteAsMarkdownLink = true, + isExternalLink = false, options?: UriListSnippetOptions, ): { snippet: vscode.SnippetString; label: string } | undefined { if (!uris.length) { return; } - const dir = getDocumentDir(document); - - const snippet = new vscode.SnippetString(); + const documentDir = getDocumentDir(document.uri); + let snippet = new vscode.SnippetString(); let insertedLinkCount = 0; let insertedImageCount = 0; let insertedAudioVideoCount = 0; uris.forEach((uri, i) => { - const mdPath = getMdPath(dir, uri); + const mdPath = getMdPath(documentDir, uri); const ext = URI.Utils.extname(uri).toLowerCase().replace('.', ''); const insertAsMedia = typeof options?.insertAsMedia === 'undefined' ? mediaFileExtensions.has(ext) : !!options.insertAsMedia; @@ -149,19 +259,23 @@ export function createUriListSnippet( snippet.appendText(`'); - } else { + } else if (insertAsMedia) { if (insertAsMedia) { insertedImageCount++; - snippet.appendText('!['); - const placeholderText = options?.placeholderText ? (escapeBrackets(title) || 'Alt text') : 'label'; - const placeholderIndex = typeof options?.placeholderStartIndex !== 'undefined' ? options?.placeholderStartIndex + i : (placeholderValue === 0 ? undefined : placeholderValue); - snippet.appendPlaceholder(placeholderText, placeholderIndex); - snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); - } else { - insertedLinkCount++; - snippet.appendText('['); - snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); - snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); + if (pasteAsMarkdownLink) { + snippet.appendText('!['); + const placeholderText = escapeBrackets(title) || options?.placeholderText || 'Alt text'; + const placeholderIndex = typeof options?.placeholderStartIndex !== 'undefined' ? options?.placeholderStartIndex + i : (placeholderValue === 0 ? undefined : placeholderValue); + snippet.appendPlaceholder(placeholderText, placeholderIndex); + snippet.appendText(`](${escapeMarkdownLinkPath(mdPath, isExternalLink)})`); + } else { + snippet.appendText(escapeMarkdownLinkPath(mdPath, isExternalLink)); + } + } + } else { + insertedLinkCount++; + if (uriStrings) { + snippet = appendToLinkSnippet(snippet, pasteAsMarkdownLink, mdPath, title, uriStrings[i], placeholderValue, isExternalLink); } } @@ -282,17 +396,16 @@ function escapeHtmlAttribute(attr: string): string { return encodeURI(attr).replaceAll('"', '"'); } -function escapeMarkdownLinkPath(mdPath: string): string { +function escapeMarkdownLinkPath(mdPath: string, isExternalLink: boolean): string { if (needsBracketLink(mdPath)) { return '<' + mdPath.replaceAll('<', '\\<').replaceAll('>', '\\>') + '>'; } - return encodeURI(mdPath); + return isExternalLink ? mdPath : encodeURI(mdPath); } function escapeBrackets(value: string): string { value = value.replace(/[\[\]]/g, '\\$&'); - // value = value.replace(/\r\n\r\n/g, '\n\n'); return value; } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 282cef1548d..cd999c44116 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -114,6 +114,7 @@ export class MarkdownItEngine implements IMdParser { _contributionProvider.onContributionsChanged(() => { // Markdown plugin contributions may have changed this._md = undefined; + this._tokenCache.clean(); }); } diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index 57ec4ee0051..331fb5566a0 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -103,7 +103,7 @@ export class MdDocumentRenderer { ${this._getStyles(resourceProvider, sourceUri, config, imageInfo)} - + ${body.html} ${this._getScripts(resourceProvider, nonce)} diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index de58e54784a..7ccbc625b47 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -95,7 +95,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } this._register(_contributionProvider.onContributionsChanged(() => { - setTimeout(() => this.refresh(), 0); + setTimeout(() => this.refresh(true), 0); })); this._register(vscode.workspace.onDidChangeTextDocument(event => { diff --git a/extensions/markdown-language-features/src/test/markdownLink.test.ts b/extensions/markdown-language-features/src/test/markdownLink.test.ts new file mode 100644 index 00000000000..085d33592d0 --- /dev/null +++ b/extensions/markdown-language-features/src/test/markdownLink.test.ts @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as assert from 'assert'; +import 'mocha'; +import { SkinnyTextDocument, checkSmartPaste, createEditAddingLinksForUriList, appendToLinkSnippet, validateLink } from '../languageFeatures/copyFiles/shared'; +suite('createEditAddingLinksForUriList', () => { + + test('Markdown Link Pasting should occur for a valid link (end to end)', async () => { + // createEditAddingLinksForUriList -> checkSmartPaste -> tryGetUriListSnippet -> createUriListSnippet -> createLinkSnippet + + const skinnyDocument: SkinnyTextDocument = { + uri: vscode.Uri.parse('file:///path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return 'hello world!'; }, + // lineAt: function (position: vscode.Position) { + // return { + // lineNumber: 0, + // text: 'hello world!', + // range: new vscode.Range(position, position), + // rangeIncludingLineBreak: new vscode.Range(position, position), + // firstNonWhitespaceCharacterIndex: 0, + // isEmptyOrWhitespace: false + // } as vscode.TextLine; + // } + }; + + const result = await createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true, new vscode.CancellationTokenSource().token); + // need to check the actual result -> snippet value + assert.strictEqual(result?.label, 'Insert Markdown Link'); + }); + + suite('validateLink', () => { + + test('Markdown pasting should occur for a valid link.', () => { + const isLink = validateLink('https://www.microsoft.com/').isValid; + assert.strictEqual(isLink, true); + }); + + test('Markdown pasting should occur for a valid link preceded by a new line.', () => { + const isLink = validateLink('\r\nhttps://www.microsoft.com/').isValid; + assert.strictEqual(isLink, true); + }); + + test('Markdown pasting should occur for a valid link followed by a new line.', () => { + const isLink = validateLink('https://www.microsoft.com/\r\n').isValid; + assert.strictEqual(isLink, true); + }); + + test('Markdown pasting should not occur for a valid hostname and invalid protool.', () => { + const isLink = validateLink('invalid:www.microsoft.com').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for plain text.', () => { + const isLink = validateLink('hello world!').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for plain text including a colon.', () => { + const isLink = validateLink('hello: world!').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for plain text including a slashes.', () => { + const isLink = validateLink('helloworld!').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for a link followed by text.', () => { + const isLink = validateLink('https://www.microsoft.com/ hello world!').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should occur for a link preceded or followed by spaces.', () => { + const isLink = validateLink(' https://www.microsoft.com/ ').isValid; + assert.strictEqual(isLink, true); + }); + + test('Markdown pasting should not occur for a link with an invalid scheme.', () => { + const isLink = validateLink('hello:www.microsoft.com').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for multiple links being pasted.', () => { + const isLink = validateLink('https://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for multiple links with spaces being pasted.', () => { + const isLink = validateLink('https://www.microsoft.com/ \r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\n hello \r\nhttps://www.microsoft.com/').isValid; + assert.strictEqual(isLink, false); + }); + + test('Markdown pasting should not occur for just a valid uri scheme', () => { + const isLink = validateLink('https://').isValid; + assert.strictEqual(isLink, false); + }); + }); + + suite('appendToLinkSnippet', () => { + test('Should create auto link when pasted link has an mismatched parentheses', () => { + const uriString = 'https://www.mic(rosoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), false, 'https:/www.microsoft.com', '', uriString, 0, true); + assert.strictEqual(snippet?.value, ''); + }); + + test('Should create snippet with < > when pasted link has an mismatched parentheses', () => { + const uriString = 'https://www.mic(rosoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', 'abc', uriString, 0, true); + assert.strictEqual(snippet?.value, '[${0:abc}]()'); + }); + + test('Should not create Markdown link snippet when pasteAsMarkdownLink is false', () => { + const uriString = 'https://www.microsoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), false, 'https:/www.microsoft.com', '', uriString, 0, true); + assert.strictEqual(snippet?.value, 'https://www.microsoft.com'); + }); + + test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => { + const uriString = 'https://www.microsoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uriString, 0, true); + assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com)'); + }); + + test('Should use an unencoded URI string in Markdown link when passing in an external browser link', () => { + const uriString = 'https://www.microsoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uriString, 0, true); + assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com)'); + }); + + test('Should not decode an encoded URI string when passing in an external browser link', () => { + const uriString = 'https://www.microsoft.com/%20'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uriString, 0, true); + assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/%20)'); + }); + + test('Should not encode an unencoded URI string when passing in an external browser link', () => { + const uriString = 'https://www.example.com/path?query=value&another=value#fragment'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uriString, 0, true); + assert.strictEqual(snippet?.value, '[${0:Title}](https://www.example.com/path?query=value&another=value#fragment)'); + }); + }); + + + suite('checkSmartPaste', () => { + + const skinnyDocument: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return 'hello world!'; }, + }; + + test('Should evaluate pasteAsMarkdownLink as true for selected plain text', () => { + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 12), new vscode.Range(0, 0, 0, 12)); + assert.strictEqual(pasteAsMarkdownLink, true); + }); + + test('Should evaluate pasteAsMarkdownLink as false for a valid selected link', () => { + skinnyDocument.getText = function () { return 'https://www.microsoft.com'; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 25), new vscode.Range(0, 0, 0, 25)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + test('Should evaluate pasteAsMarkdownLink as false for a valid selected link with trailing whitespace', () => { + skinnyDocument.getText = function () { return ' https://www.microsoft.com '; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 30), new vscode.Range(0, 0, 0, 30)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + test('Should evaluate pasteAsMarkdownLink as true for a link pasted in square brackets', () => { + skinnyDocument.getText = function () { return '[abc]'; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 1, 0, 4), new vscode.Range(0, 1, 0, 4)); + assert.strictEqual(pasteAsMarkdownLink, true); + }); + + test('Should evaluate pasteAsMarkdownLink as false for no selection', () => { + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 0), new vscode.Range(0, 0, 0, 0)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + test('Should evaluate pasteAsMarkdownLink as false for selected whitespace and new lines', () => { + skinnyDocument.getText = function () { return ' \r\n\r\n'; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 7), new vscode.Range(0, 0, 0, 7)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a backtick code block', () => { + skinnyDocument.getText = function () { return '```\r\n\r\n```'; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 5, 0, 5), new vscode.Range(0, 5, 0, 5)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a tilde code block', () => { + skinnyDocument.getText = function () { return '~~~\r\n\r\n~~~'; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 5, 0, 5), new vscode.Range(0, 5, 0, 5)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a math block', () => { + skinnyDocument.getText = function () { return '$$$\r\n\r\n$$$'; }; + const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 5, 0, 5), new vscode.Range(0, 5, 0, 5)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + const linkSkinnyDoc: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '[a](bcdef)'; }, + }; + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown link', () => { + const pasteAsMarkdownLink = checkSmartPaste(linkSkinnyDoc, new vscode.Range(0, 4, 0, 6), new vscode.Range(0, 4, 0, 6)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + + const imageLinkSkinnyDoc: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '![a](bcdef)'; }, + }; + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown image link', () => { + const pasteAsMarkdownLink = checkSmartPaste(imageLinkSkinnyDoc, new vscode.Range(0, 5, 0, 10), new vscode.Range(0, 5, 0, 10)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + const inlineCodeSkinnyCode: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '``'; }, + }; + + test('Should evaluate pasteAsMarkdownLink as false for pasting within inline code', () => { + const pasteAsMarkdownLink = checkSmartPaste(inlineCodeSkinnyCode, new vscode.Range(0, 1, 0, 1), new vscode.Range(0, 1, 0, 1)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + + const inlineMathSkinnyDoc: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '$$'; }, + }; + + test('Should evaluate pasteAsMarkdownLink as false for pasting within inline math', () => { + const pasteAsMarkdownLink = checkSmartPaste(inlineMathSkinnyDoc, new vscode.Range(0, 1, 0, 1), new vscode.Range(0, 1, 0, 1)); + assert.strictEqual(pasteAsMarkdownLink, false); + }); + }); +}); diff --git a/extensions/markdown-language-features/src/util/document.ts b/extensions/markdown-language-features/src/util/document.ts index 9c192227ee3..856226a7376 100644 --- a/extensions/markdown-language-features/src/util/document.ts +++ b/extensions/markdown-language-features/src/util/document.ts @@ -7,24 +7,24 @@ import * as vscode from 'vscode'; import { Schemes } from './schemes'; import { Utils } from 'vscode-uri'; -export function getDocumentDir(document: vscode.TextDocument): vscode.Uri | undefined { - const docUri = getParentDocumentUri(document); +export function getDocumentDir(uri: vscode.Uri): vscode.Uri | undefined { + const docUri = getParentDocumentUri(uri); if (docUri.scheme === Schemes.untitled) { return vscode.workspace.workspaceFolders?.[0]?.uri; } return Utils.dirname(docUri); } -export function getParentDocumentUri(document: vscode.TextDocument): vscode.Uri { - if (document.uri.scheme === Schemes.notebookCell) { +export function getParentDocumentUri(uri: vscode.Uri): vscode.Uri { + if (uri.scheme === Schemes.notebookCell) { for (const notebook of vscode.workspace.notebookDocuments) { for (const cell of notebook.getCells()) { - if (cell.document === document) { + if (cell.document.uri.toString() === uri.toString()) { return notebook.uri; } } } } - return document.uri; + return uri; } diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index e796e655bae..8a47f870e93 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -165,10 +165,10 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -"@types/dompurify@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.3.1.tgz#2934adcd31c4e6b02676f9c22f9756e5091c04dd" - integrity sha512-YJth9qa0V/E6/XPH1Jq4BC8uCMmO8V1fKWn8PCvuZcAhMn7q0ez9LW6naQT04UZzjFfAPhyRMZmI2a2rbMlEFA== +"@types/dompurify@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.2.tgz#c1cd33a475bc49c43c2a7900e41028e2136a4553" + integrity sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ== dependencies: "@types/trusted-types" "*" @@ -352,10 +352,10 @@ diagnostic-channel@1.1.0: dependencies: semver "^5.3.0" -dompurify@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.1.tgz#f9cb1a275fde9af6f2d0a2644ef648dd6847b631" - integrity sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA== +dompurify@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.5.tgz#eb3d9cfa10037b6e73f32c586682c4b2ab01fbed" + integrity sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A== emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -378,10 +378,10 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -highlight.js@^11.4.0: - version "11.4.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.4.0.tgz#34ceadd49e1596ee5aba3d99346cdfd4845ee05a" - integrity sha512-nawlpCBCSASs7EdvZOYOYVkJpGmAOKMYZgZtUqSRqodZE0GRVcFKwo1RcpeOemqh9hyttTdd5wDBwHkuSyUfnA== +highlight.js@^11.8.0: + version "11.8.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65" + integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg== http-proxy-agent@^5.0.0: version "5.0.0" @@ -475,14 +475,14 @@ picomatch@^2.3.1: integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" diff --git a/extensions/media-preview/yarn.lock b/extensions/media-preview/yarn.lock index 2fb59ea6fef..971c4c2c50a 100644 --- a/extensions/media-preview/yarn.lock +++ b/extensions/media-preview/yarn.lock @@ -318,9 +318,9 @@ ms@2.1.2: integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" diff --git a/extensions/merge-conflict/yarn.lock b/extensions/merge-conflict/yarn.lock index 8625415ddeb..d07fe97f099 100644 --- a/extensions/merge-conflict/yarn.lock +++ b/extensions/merge-conflict/yarn.lock @@ -323,9 +323,9 @@ ms@2.1.2: integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index 5c62a11cfa4..cd9ae79d100 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -376,9 +376,9 @@ node-fetch@2.6.7: whatwg-url "^5.0.0" semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index df674353633..090e9719420 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-notebook-renderer'; -import { createOutputContent, scrollableClass } from './textHelper'; -import { HtmlRenderingHook, IDisposable, IRichRenderContext, JavaScriptRenderingHook, RenderOptions } from './rendererTypes'; +import { createOutputContent, appendOutput, scrollableClass } from './textHelper'; +import { HtmlRenderingHook, IDisposable, IRichRenderContext, JavaScriptRenderingHook, OutputWithAppend, RenderOptions } from './rendererTypes'; import { ttPolicy } from './htmlHelper'; function clearContainer(container: HTMLElement) { @@ -152,7 +152,7 @@ function renderError( outputInfo: OutputItem, outputElement: HTMLElement, ctx: IRichRenderContext, - trustHTML: boolean + trustHtml: boolean ): IDisposable { const disposableStore = createDisposableStore(); @@ -172,7 +172,7 @@ function renderError( outputElement.classList.add('traceback'); const outputScrolling = scrollingEnabled(outputInfo, ctx.settings); - const content = createOutputContent(outputInfo.id, [err.stack ?? ''], ctx.settings.lineLimit, outputScrolling, trustHTML); + const content = createOutputContent(outputInfo.id, err.stack ?? '', { linesLimit: ctx.settings.lineLimit, scrollable: outputScrolling, trustHtml }); const contentParent = document.createElement('div'); contentParent.classList.toggle('word-wrap', ctx.settings.outputWordWrap); disposableStore.push(ctx.onDidChangeSettings(e => { @@ -270,19 +270,13 @@ function scrollingEnabled(output: OutputItem, options: RenderOptions) { // div.output.output-stream <-- outputElement parameter // div.scrollable? tabindex="0" <-- contentParent // div output-item-id="{guid}" <-- content from outputItem parameter -function renderStream(outputInfo: OutputItem, outputElement: HTMLElement, error: boolean, ctx: IRichRenderContext): IDisposable { +function renderStream(outputInfo: OutputWithAppend, outputElement: HTMLElement, error: boolean, ctx: IRichRenderContext): IDisposable { const disposableStore = createDisposableStore(); const outputScrolling = scrollingEnabled(outputInfo, ctx.settings); + const outputOptions = { linesLimit: ctx.settings.lineLimit, scrollable: outputScrolling, trustHtml: false, error }; outputElement.classList.add('output-stream'); - const text = outputInfo.text(); - const newContent = createOutputContent(outputInfo.id, [text], ctx.settings.lineLimit, outputScrolling, false); - newContent.setAttribute('output-item-id', outputInfo.id); - if (error) { - newContent.classList.add('error'); - } - const scrollTop = outputScrolling ? findScrolledHeight(outputElement) : undefined; const previousOutputParent = getPreviousMatchingContentGroup(outputElement); @@ -290,9 +284,9 @@ function renderStream(outputInfo: OutputItem, outputElement: HTMLElement, error: if (previousOutputParent) { const existingContent = previousOutputParent.querySelector(`[output-item-id="${outputInfo.id}"]`) as HTMLElement | null; if (existingContent) { - existingContent.replaceWith(newContent); - + appendOutput(outputInfo, existingContent, outputOptions); } else { + const newContent = createOutputContent(outputInfo.id, outputInfo.text(), outputOptions); previousOutputParent.appendChild(newContent); } previousOutputParent.classList.toggle('scrollbar-visible', previousOutputParent.scrollHeight > previousOutputParent.clientHeight); @@ -301,12 +295,9 @@ function renderStream(outputInfo: OutputItem, outputElement: HTMLElement, error: const existingContent = outputElement.querySelector(`[output-item-id="${outputInfo.id}"]`) as HTMLElement | null; let contentParent = existingContent?.parentElement; if (existingContent && contentParent) { - existingContent.replaceWith(newContent); - while (newContent.nextSibling) { - // clear out any stale content if we had previously combined streaming outputs into this one - newContent.nextSibling.remove(); - } + appendOutput(outputInfo, existingContent, outputOptions); } else { + const newContent = createOutputContent(outputInfo.id, outputInfo.text(), outputOptions); contentParent = document.createElement('div'); contentParent.appendChild(newContent); while (outputElement.firstChild) { @@ -333,7 +324,7 @@ function renderText(outputInfo: OutputItem, outputElement: HTMLElement, ctx: IRi const text = outputInfo.text(); const outputScrolling = scrollingEnabled(outputInfo, ctx.settings); - const content = createOutputContent(outputInfo.id, [text], ctx.settings.lineLimit, outputScrolling, false); + const content = createOutputContent(outputInfo.id, text, { linesLimit: ctx.settings.lineLimit, scrollable: outputScrolling, trustHtml: false }); content.classList.add('output-plaintext'); if (ctx.settings.outputWordWrap) { content.classList.add('word-wrap'); diff --git a/extensions/notebook-renderers/src/rendererTypes.ts b/extensions/notebook-renderers/src/rendererTypes.ts index 9da94aeef5d..ded12bdcacc 100644 --- a/extensions/notebook-renderers/src/rendererTypes.ts +++ b/extensions/notebook-renderers/src/rendererTypes.ts @@ -35,3 +35,14 @@ export interface RenderOptions { } export type IRichRenderContext = RendererContext & { readonly settings: RenderOptions; readonly onDidChangeSettings: Event }; + +export type OutputElementOptions = { + linesLimit: number; + scrollable?: boolean; + error?: boolean; + trustHtml?: boolean; +}; + +export interface OutputWithAppend extends OutputItem { + appendedText?(): string | undefined; +} diff --git a/extensions/notebook-renderers/src/test/notebookRenderer.test.ts b/extensions/notebook-renderers/src/test/notebookRenderer.test.ts index e67d1d8ce26..0f747900377 100644 --- a/extensions/notebook-renderers/src/test/notebookRenderer.test.ts +++ b/extensions/notebook-renderers/src/test/notebookRenderer.test.ts @@ -5,8 +5,8 @@ import * as assert from 'assert'; import { activate } from '..'; -import { OutputItem, RendererApi } from 'vscode-notebook-renderer'; -import { IDisposable, IRichRenderContext, RenderOptions } from '../rendererTypes'; +import { RendererApi } from 'vscode-notebook-renderer'; +import { IDisposable, IRichRenderContext, OutputWithAppend, RenderOptions } from '../rendererTypes'; import { JSDOM } from "jsdom"; const dom = new JSDOM(); @@ -116,10 +116,13 @@ suite('Notebook builtin output renderer', () => { } } - function createOutputItem(text: string, mime: string, id: string = '123'): OutputItem { + function createOutputItem(text: string, mime: string, id: string = '123', appendedText?: string): OutputWithAppend { return { id: id, mime: mime, + appendedText() { + return appendedText; + }, text() { return text; }, @@ -177,9 +180,9 @@ suite('Notebook builtin output renderer', () => { assert.ok(renderer, 'Renderer not created'); const outputElement = new OutputHtml().getFirstOuputElement(); - const outputItem = createOutputItem('content', 'text/plain'); + const outputItem = createOutputItem('content', mimeType); await renderer!.renderOutputItem(outputItem, outputElement); - const outputItem2 = createOutputItem('replaced content', 'text/plain'); + const outputItem2 = createOutputItem('replaced content', mimeType); await renderer!.renderOutputItem(outputItem2, outputElement); const inserted = outputElement.firstChild as HTMLElement; @@ -189,6 +192,87 @@ suite('Notebook builtin output renderer', () => { }); + test('Append streaming output', async () => { + const context = createContext({ outputWordWrap: false, outputScrolling: true }); + const renderer = await activate(context); + assert.ok(renderer, 'Renderer not created'); + + const outputElement = new OutputHtml().getFirstOuputElement(); + const outputItem = createOutputItem('content', stdoutMimeType, '123', 'ignoredAppend'); + await renderer!.renderOutputItem(outputItem, outputElement); + const outputItem2 = createOutputItem('content\nappended', stdoutMimeType, '123', '\nappended'); + await renderer!.renderOutputItem(outputItem2, outputElement); + + const inserted = outputElement.firstChild as HTMLElement; + assert.ok(inserted.innerHTML.indexOf('>contentappendedcontentcontent { + const context = createContext({ outputScrolling: true }); + const renderer = await activate(context); + assert.ok(renderer, 'Renderer not created'); + + const outputHtml = new OutputHtml(); + const firstOutputElement = outputHtml.getFirstOuputElement(); + const outputItem1 = createOutputItem('first stream content', stdoutMimeType, '1'); + const outputItem2 = createOutputItem(JSON.stringify(error), errorMimeType, '2'); + const outputItem3 = createOutputItem('second stream content', stdoutMimeType, '3'); + await renderer!.renderOutputItem(outputItem1, firstOutputElement); + const secondOutputElement = outputHtml.appendOutputElement(); + await renderer!.renderOutputItem(outputItem2, secondOutputElement); + const thirdOutputElement = outputHtml.appendOutputElement(); + await renderer!.renderOutputItem(outputItem3, thirdOutputElement); + + const appendedItem1 = createOutputItem('', stdoutMimeType, '1', ' appended1'); + await renderer!.renderOutputItem(appendedItem1, firstOutputElement); + const appendedItem3 = createOutputItem('', stdoutMimeType, '3', ' appended3'); + await renderer!.renderOutputItem(appendedItem3, thirdOutputElement); + + assert.ok(firstOutputElement.innerHTML.indexOf('>first stream content') > -1, `Content was not added to output element: ${outputHtml.cellElement.innerHTML}`); + assert.ok(firstOutputElement.innerHTML.indexOf('appended1') > -1, `Content was not appended to output element: ${outputHtml.cellElement.innerHTML}`); + assert.ok(secondOutputElement.innerHTML.indexOf('>NameError -1, `Content was not added to output element: ${outputHtml.cellElement.innerHTML}`); + assert.ok(thirdOutputElement.innerHTML.indexOf('>second stream content') > -1, `Content was not added to output element: ${outputHtml.cellElement.innerHTML}`); + assert.ok(thirdOutputElement.innerHTML.indexOf('appended3') > -1, `Content was not appended to output element: ${outputHtml.cellElement.innerHTML}`); + }); + + test('Append large streaming outputs', async () => { + const context = createContext({ outputWordWrap: false, outputScrolling: true }); + const renderer = await activate(context); + assert.ok(renderer, 'Renderer not created'); + + const outputElement = new OutputHtml().getFirstOuputElement(); + const lotsOfLines = new Array(4998).fill('line').join('\n'); + const firstOuput = lotsOfLines + 'expected1'; + const outputItem = createOutputItem(firstOuput, stdoutMimeType, '123'); + await renderer!.renderOutputItem(outputItem, outputElement); + const appended = '\n' + lotsOfLines + 'expectedAppend'; + const outputItem2 = createOutputItem(firstOuput + appended, stdoutMimeType, '123', appended); + await renderer!.renderOutputItem(outputItem2, outputElement); + + const inserted = outputElement.firstChild as HTMLElement; + assert.ok(inserted.innerHTML.indexOf('expected1') !== -1, `Last bit of previous content should still exist`); + assert.ok(inserted.innerHTML.indexOf('expectedAppend') !== -1, `Content was not appended to output element`); + }); + + test('Streaming outputs larger than the line limit are truncated', async () => { + const context = createContext({ outputWordWrap: false, outputScrolling: true }); + const renderer = await activate(context); + assert.ok(renderer, 'Renderer not created'); + + const outputElement = new OutputHtml().getFirstOuputElement(); + const lotsOfLines = new Array(11000).fill('line').join('\n'); + const firstOuput = 'shouldBeTruncated' + lotsOfLines + 'expected1'; + const outputItem = createOutputItem(firstOuput, stdoutMimeType, '123'); + await renderer!.renderOutputItem(outputItem, outputElement); + + const inserted = outputElement.firstChild as HTMLElement; + assert.ok(inserted.innerHTML.indexOf('expected1') !== -1, `Last bit of content should exist`); + assert.ok(inserted.innerHTML.indexOf('shouldBeTruncated') === -1, `Beginning content should be truncated`); + }); + test(`Render with wordwrap and scrolling for error output`, async () => { const context = createContext({ outputWordWrap: true, outputScrolling: true }); const renderer = await activate(context); @@ -268,6 +352,29 @@ suite('Notebook builtin output renderer', () => { assert.ok(inserted.innerHTML.indexOf('>second stream content { + const context = createContext({ outputScrolling: true }); + const renderer = await activate(context); + assert.ok(renderer, 'Renderer not created'); + + const outputHtml = new OutputHtml(); + const outputElement = outputHtml.getFirstOuputElement(); + const outputItem1 = createOutputItem('first stream content', stdoutMimeType, '1'); + const outputItem2 = createOutputItem('second stream content', stdoutMimeType, '2'); + await renderer!.renderOutputItem(outputItem1, outputElement); + const secondOutput = outputHtml.appendOutputElement(); + await renderer!.renderOutputItem(outputItem2, secondOutput); + const appendingOutput = createOutputItem('', stdoutMimeType, '2', ' appended'); + await renderer!.renderOutputItem(appendingOutput, secondOutput); + + + const inserted = outputElement.firstChild as HTMLElement; + assert.ok(inserted, `nothing appended to output element: ${outputElement.innerHTML}`); + assert.ok(inserted.innerHTML.indexOf('>first stream content -1, `Content was not added to output element: ${outputHtml.cellElement.innerHTML}`); + assert.ok(inserted.innerHTML.indexOf('>second stream content') > -1, `Second content was not added to ouptut element: ${outputHtml.cellElement.innerHTML}`); + assert.ok(inserted.innerHTML.indexOf('appended') > -1, `Content was not appended to ouptut element: ${outputHtml.cellElement.innerHTML}`); + }); + test(`Streaming outputs interleaved with other mime types will produce separate outputs`, async () => { const context = createContext({ outputScrolling: false }); const renderer = await activate(context); diff --git a/extensions/notebook-renderers/src/textHelper.ts b/extensions/notebook-renderers/src/textHelper.ts index 8cc03fd543e..5cf0e24eb96 100644 --- a/extensions/notebook-renderers/src/textHelper.ts +++ b/extensions/notebook-renderers/src/textHelper.ts @@ -4,9 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { handleANSIOutput } from './ansi'; - +import { OutputElementOptions, OutputWithAppend } from './rendererTypes'; export const scrollableClass = 'scrollable'; +const softScrollableLineLimit = 5000; +const hardScrollableLineLimit = 8000; + /** * Output is Truncated. View as a [scrollable element] or open in a [text editor]. Adjust cell output [settings...] */ @@ -91,22 +94,70 @@ function truncatedArrayOfString(id: string, buffer: string[], linesLimit: number function scrollableArrayOfString(id: string, buffer: string[], trustHtml: boolean) { const element = document.createElement('div'); - if (buffer.length > 5000) { + if (buffer.length > softScrollableLineLimit) { element.appendChild(generateNestedViewAllElement(id)); } - element.appendChild(handleANSIOutput(buffer.slice(-5000).join('\n'), trustHtml)); + element.appendChild(handleANSIOutput(buffer.slice(-1 * softScrollableLineLimit).join('\n'), trustHtml)); return element; } -export function createOutputContent(id: string, outputs: string[], linesLimit: number, scrollable: boolean, trustHtml: boolean): HTMLElement { +const outputLengths: Record = {}; - const buffer = outputs.join('\n').split(/\r\n|\r|\n/g); - - if (scrollable) { - return scrollableArrayOfString(id, buffer, trustHtml); - } else { - return truncatedArrayOfString(id, buffer, linesLimit, trustHtml); +function appendScrollableOutput(element: HTMLElement, id: string, appended: string, trustHtml: boolean) { + if (!outputLengths[id]) { + outputLengths[id] = 0; } + + const buffer = appended.split(/\r\n|\r|\n/g); + const appendedLength = buffer.length + outputLengths[id]; + // Only append outputs up to the hard limit of lines, then replace it with the last softLimit number of lines + if (appendedLength > hardScrollableLineLimit) { + return false; + } + else { + element.appendChild(handleANSIOutput(buffer.join('\n'), trustHtml)); + outputLengths[id] = appendedLength; + } + return true; } + +export function createOutputContent(id: string, outputText: string, options: OutputElementOptions): HTMLElement { + const { linesLimit, error, scrollable, trustHtml } = options; + const buffer = outputText.split(/\r\n|\r|\n/g); + outputLengths[id] = outputLengths[id] = Math.min(buffer.length, softScrollableLineLimit); + + let outputElement: HTMLElement; + if (scrollable) { + outputElement = scrollableArrayOfString(id, buffer, !!trustHtml); + } else { + outputElement = truncatedArrayOfString(id, buffer, linesLimit, !!trustHtml); + } + + outputElement.setAttribute('output-item-id', id); + if (error) { + outputElement.classList.add('error'); + } + + return outputElement; +} + +export function appendOutput(outputInfo: OutputWithAppend, existingContent: HTMLElement, options: OutputElementOptions) { + const appendedText = outputInfo.appendedText?.(); + // appending output only supported for scrollable ouputs currently + if (appendedText && options.scrollable) { + if (appendScrollableOutput(existingContent, outputInfo.id, appendedText, false)) { + return; + } + } + + const newContent = createOutputContent(outputInfo.id, outputInfo.text(), options); + existingContent.replaceWith(newContent); + while (newContent.nextSibling) { + // clear out any stale content if we had previously combined streaming outputs into this one + newContent.nextSibling.remove(); + } + +} + diff --git a/extensions/notebook-renderers/yarn.lock b/extensions/notebook-renderers/yarn.lock index ba3ff6b48d9..fac17bd8f60 100644 --- a/extensions/notebook-renderers/yarn.lock +++ b/extensions/notebook-renderers/yarn.lock @@ -334,9 +334,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tough-cookie@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" diff --git a/extensions/package.json b/extensions/package.json index 8ab93875293..32f5bc84b98 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "5.1.3" + "typescript": "^5.2.0-dev.20230731" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/perl/package.json b/extensions/perl/package.json index 8e3fed18d0c..3357e1ff3bd 100644 --- a/extensions/perl/package.json +++ b/extensions/perl/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/perl.tmbundle Syntaxes/Perl.plist ./syntaxes/perl.tmLanguage.json Syntaxes/Perl%206.tmLanguage ./syntaxes/perl6.tmLanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/perl.tmbundle Syntaxes/Perl.plist ./syntaxes/perl.tmLanguage.json Syntaxes/Perl%%206.tmLanguage ./syntaxes/perl6.tmLanguage.json" }, "contributes": { "languages": [ diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index e585ebf1c91..f44d7a25cb6 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -76,6 +76,15 @@ "indent": "none", "removeText": 1 } + }, + { + // Decrease indentation after single line if/else if/else, for, foreach, or while + "previousLineText": "^\\s*(((else ?)?if|for(each)?|while)\\s*\\(.*\\)\\s*|else\\s*)$", + // But make sure line doesn't have braces or is not another if statement + "beforeText": "^\\s+([^{i\\s]|i(?!f\\b))", + "action": { + "indent": "outdent" + } } ] } diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 90443f16771..815bd8c1391 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -78,7 +78,7 @@ export function activate(context: vscode.ExtensionContext) { const lineResult = parseSearchResults(document, token)[position.line]; if (!lineResult) { return []; } if (lineResult.type === 'file') { - return lineResult.allLocations; + return lineResult.allLocations.map(l => ({ ...l, originSelectionRange: lineResult.location.originSelectionRange })); } const location = lineResult.locations.find(l => l.originSelectionRange.contains(position)); diff --git a/extensions/shellscript/cgmanifest.json b/extensions/shellscript/cgmanifest.json index 87be4976392..f246d45fe21 100644 --- a/extensions/shellscript/cgmanifest.json +++ b/extensions/shellscript/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/better-shell-syntax", "repositoryUrl": "https://github.com/jeff-hykin/better-shell-syntax", - "commitHash": "1bad17d8badf6283125aaa7c31be06ba64146a0f" + "commitHash": "ce62ea59e8e522f8a07d8d8a2d1f992c6c600b91" } }, "license": "MIT", - "version": "1.5.4" + "version": "1.6.2" } ], "version": 1 diff --git a/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json b/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json index e132b9e5699..68055eb7b29 100644 --- a/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json +++ b/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/better-shell-syntax/commit/1bad17d8badf6283125aaa7c31be06ba64146a0f", + "version": "https://github.com/jeff-hykin/better-shell-syntax/commit/ce62ea59e8e522f8a07d8d8a2d1f992c6c600b91", "name": "Shell Script", "scopeName": "source.shell", "patterns": [ @@ -14,7 +14,7 @@ ], "repository": { "alias_statement": { - "begin": "(alias)[ \\t]*+[ \\t]*+(?:((?<=^|;|&|[ \\t])(?:export|declare|typeset|local|readonly)(?=[ \\t]|;|&|$))[ \\t]*+)?((?|#|\\n|$|;|[ \\t]))(?!foreach\\b(?!\\/)|select\\b(?!\\/)|repeat\\b(?!\\/)|until\\b(?!\\/)|while\\b(?!\\/)|case\\b(?!\\/)|done\\b(?!\\/)|elif\\b(?!\\/)|else\\b(?!\\/)|esac\\b(?!\\/)|then\\b(?!\\/)|for\\b(?!\\/)|end\\b(?!\\/)|in\\b(?!\\/)|fi\\b(?!\\/)|do\\b(?!\\/)|if\\b(?!\\/))(?:((?<=^|;|&|[ \\t])(?:export|declare|typeset|local|readonly)(?=[ \\t]|;|&|$))|((?!\"|'|\\\\\\n?$)[^!'\" \\t\\n\\r]+?))(?:(?= |\\t)|(?=;|\\||&|\\n|\\)|\\`|\\{|\\}|[ \\t]*#|\\])(?|#|\\n|$|;|[ \\t]))(?!foreach\\b(?!\\/)|select\\b(?!\\/)|repeat\\b(?!\\/)|until\\b(?!\\/)|while\\b(?!\\/)|case\\b(?!\\/)|done\\b(?!\\/)|elif\\b(?!\\/)|else\\b(?!\\/)|esac\\b(?!\\/)|then\\b(?!\\/)|for\\b(?!\\/)|end\\b(?!\\/)|in\\b(?!\\/)|fi\\b(?!\\/)|do\\b(?!\\/)|if\\b(?!\\/))(?:((?<=^|;|&|[ \\t])(?:readonly|declare|typeset|export|local)(?=[ \\t]|;|&|$))|((?!\"|'|\\\\\\n?$)[^!'\" \\t\\n\\r]+?))(?:(?= |\\t)|(?=;|\\||&|\\n|\\)|\\`|\\{|\\}|[ \\t]*#|\\])(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1805,7 +1873,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1966,7 +2034,7 @@ "name": "storage.type.namespace.ts" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2003,7 +2071,7 @@ "name": "entity.name.type.alias.ts" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2021,7 +2089,7 @@ "name": "keyword.control.intrinsic.ts" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2035,7 +2103,7 @@ "name": "keyword.operator.assignment.ts" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2226,7 +2294,7 @@ "name": "keyword.control.default.ts" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2238,7 +2306,7 @@ }, { "name": "meta.export.ts", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3953,7 +4021,7 @@ "name": "keyword.operator.type.annotation.ts" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3967,7 +4035,7 @@ "name": "meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.ts meta.return.type.arrow.ts", "patterns": [ { diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index 10b809d93b9..e516ea1a1fa 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/e0aefd8205cc9d1bc7859cc5babbee0d833dca0f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -134,7 +134,7 @@ "name": "keyword.control.flow.tsx" } }, - "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -299,7 +299,7 @@ { "name": "meta.var.expr.tsx", "begin": "(?=(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1808,7 +1876,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1969,7 +2037,7 @@ "name": "storage.type.namespace.tsx" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2006,7 +2074,7 @@ "name": "entity.name.type.alias.tsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2024,7 +2092,7 @@ "name": "keyword.control.intrinsic.tsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2038,7 +2106,7 @@ "name": "keyword.operator.assignment.tsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2229,7 +2297,7 @@ "name": "keyword.control.default.tsx" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2241,7 +2309,7 @@ }, { "name": "meta.export.tsx", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3904,7 +3972,7 @@ "name": "keyword.operator.type.annotation.tsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3918,7 +3986,7 @@ "name": "meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.tsx meta.return.type.arrow.tsx", "patterns": [ { diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index b9b63d61495..5f90ca1097e 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -35,7 +35,7 @@ "dependencies": { "@vscode/extension-telemetry": "0.7.5", "jsonc-parser": "^3.2.0", - "semver": "5.5.1", + "semver": "7.5.2", "vscode-tas-client": "^0.1.63", "@vscode/sync-api-client": "^0.7.2", "@vscode/sync-api-common": "^0.7.2", @@ -145,7 +145,7 @@ "properties": { "typescript.experimental.aiQuickFix": { "type": "boolean", - "default": true, + "default": false, "description": "%typescript.experimental.aiQuickFix%", "scope": "resource" }, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index bd4dd6366ad..81260c405e1 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -152,7 +152,7 @@ "typescript.preferences.includePackageJsonAutoImports.auto": "Search dependencies based on estimated performance impact.", "typescript.preferences.includePackageJsonAutoImports.on": "Always search dependencies.", "typescript.preferences.includePackageJsonAutoImports.off": "Never search dependencies.", - "typescript.preferences.autoImportFileExcludePatterns": "Specify glob patterns of files to exclude from auto imports. Requires using TypeScript 4.8 or newer in the workspace.", + "typescript.preferences.autoImportFileExcludePatterns": "Specify glob patterns of files to exclude from auto imports. Relative paths are resolved relative to the workspace root. Patterns are evaluated using tsconfig.json [`exclude`](https://www.typescriptlang.org/tsconfig#exclude) semantics. Requires using TypeScript 4.8 or newer in the workspace.", "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code.", "typescript.updateImportsOnFileMove.enabled.prompt": "Prompt on each rename.", "typescript.updateImportsOnFileMove.enabled.always": "Always update paths automatically.", diff --git a/extensions/typescript-language-features/src/configuration/fileSchemes.ts b/extensions/typescript-language-features/src/configuration/fileSchemes.ts index 93417cde8e4..9fb572c7175 100644 --- a/extensions/typescript-language-features/src/configuration/fileSchemes.ts +++ b/extensions/typescript-language-features/src/configuration/fileSchemes.ts @@ -10,6 +10,7 @@ export const file = 'file'; export const untitled = 'untitled'; export const git = 'git'; export const github = 'github'; +export const azurerepos = 'azurerepos'; /** Live share scheme */ export const vsls = 'vsls'; @@ -39,4 +40,5 @@ export const disabledSchemes = new Set([ git, vsls, github, + azurerepos, ]); diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index ebf0131fced..e3ef52002a5 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -195,6 +195,7 @@ export default class FileConfigurationManager extends Disposable { allowIncompleteCompletions: true, displayPartsForJSDoc: true, disableLineTextInReferences: true, + interactiveInlayHints: true, ...getInlayHintsPreferences(config), }; diff --git a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts index 263f8f3bd72..fb08f68dc42 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts @@ -8,7 +8,7 @@ import { DocumentSelector } from '../configuration/documentSelector'; import { LanguageDescription } from '../configuration/languageDescription'; import { API } from '../tsServer/api'; import type * as Proto from '../tsServer/protocol/protocol'; -import { Position } from '../typeConverters'; +import { Location, Position } from '../typeConverters'; import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; import { Disposable } from '../utils/dispose'; import FileConfigurationManager, { InlayHintSettingNames, getInlayHintsPreferences } from './fileConfigurationManager'; @@ -77,7 +77,7 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin return response.body.map(hint => { const result = new vscode.InlayHint( Position.fromLocation(hint.position), - hint.text, + this.convertInlayHintText(model.uri, hint), hint.kind && fromProtocolInlayHintKind(hint.kind) ); result.paddingLeft = hint.whitespaceBefore; @@ -85,6 +85,20 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin return result; }); } + + private convertInlayHintText(resource: vscode.Uri, tsHint: Proto.InlayHintItem): string | vscode.InlayHintLabelPart[] { + if (typeof tsHint.text === 'string') { + return tsHint.text; + } + + return tsHint.text.map((part): vscode.InlayHintLabelPart => { + const out = new vscode.InlayHintLabelPart(part.text); + if (part.span) { + out.location = Location.fromTextSpan(resource, part.span); + } + return out; + }); + } } function fromProtocolInlayHintKind(kind: Proto.InlayHintKind): vscode.InlayHintKind | undefined { diff --git a/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts b/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts index bc4b7ad1b5f..fe26dd64029 100644 --- a/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts +++ b/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts @@ -49,9 +49,13 @@ function getTagBodyText( return '```\n' + text + '\n```'; } - const text = convertLinkTags(tag.text, filePathConverter); + let text = convertLinkTags(tag.text, filePathConverter); switch (tag.name) { case 'example': { + // Example text does not support `{@link}` as it is considered code. + // TODO: should we support it if it appears outside of an explicit code block? + text = asPlainText(tag.text); + // check for caption tags, fix for #79704 const captionTagMatches = text.match(/(.*?)<\/caption>\s*(\r\n|\n)/); if (captionTagMatches && captionTagMatches.index === 0) { @@ -132,6 +136,13 @@ function getTagBody(tag: Proto.JSDocTagInfo, filePathConverter: IFilePathToResou return (convertLinkTags(tag.text, filePathConverter)).split(/^(\S+)\s*-?\s*/); } +function asPlainText(parts: readonly Proto.SymbolDisplayPart[] | string): string { + if (typeof parts === 'string') { + return parts; + } + return parts.map(part => part.text).join(''); +} + export function asPlainTextWithLinks( parts: readonly Proto.SymbolDisplayPart[] | string, filePathConverter: IFilePathToResourceConverter, @@ -177,10 +188,10 @@ function convertLinkTags( if (/^https?:/.test(text)) { const parts = text.split(' '); if (parts.length === 1) { - out.push(parts[0]); + out.push(`<${parts[0]}>`); } else if (parts.length > 1) { - const linkText = escapeMarkdownSyntaxTokensForCode(parts.slice(1).join(' ')); - out.push(`[${currentLink.linkcode ? '`' + linkText + '`' : linkText}](${parts[0]})`); + const linkText = parts.slice(1).join(' '); + out.push(`[${currentLink.linkcode ? '`' + escapeMarkdownSyntaxTokensForCode(linkText) + '`' : linkText}](${parts[0]})`); } } else { out.push(escapeMarkdownSyntaxTokensForCode(text)); diff --git a/extensions/typescript-language-features/src/test/unit/textRendering.test.ts b/extensions/typescript-language-features/src/test/unit/textRendering.test.ts index 2354bb7f589..b13f682f715 100644 --- a/extensions/typescript-language-features/src/test/unit/textRendering.test.ts +++ b/extensions/typescript-language-features/src/test/unit/textRendering.test.ts @@ -14,7 +14,7 @@ const noopToResource: IFilePathToResourceConverter = { }; suite('typescript.previewer', () => { - test('Should ignore hyphens after a param tag', async () => { + test('Should ignore hyphens after a param tag', () => { assert.strictEqual( tagsToMarkdown([ { @@ -25,7 +25,7 @@ suite('typescript.previewer', () => { '*@param* `a` — b'); }); - test('Should parse url jsdoc @link', async () => { + test('Should parse url jsdoc @link', () => { assert.strictEqual( documentationToMarkdown( 'x {@link http://www.example.com/foo} y {@link https://api.jquery.com/bind/#bind-eventType-eventData-handler} z', @@ -35,7 +35,7 @@ suite('typescript.previewer', () => { 'x [http://www.example.com/foo](http://www.example.com/foo) y [https://api.jquery.com/bind/#bind-eventType-eventData-handler](https://api.jquery.com/bind/#bind-eventType-eventData-handler) z'); }); - test('Should parse url jsdoc @link with text', async () => { + test('Should parse url jsdoc @link with text', () => { assert.strictEqual( documentationToMarkdown( 'x {@link http://www.example.com/foo abc xyz} y {@link http://www.example.com/bar|b a z} z', @@ -45,7 +45,7 @@ suite('typescript.previewer', () => { 'x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z'); }); - test('Should treat @linkcode jsdocs links as monospace', async () => { + test('Should treat @linkcode jsdocs links as monospace', () => { assert.strictEqual( documentationToMarkdown( 'x {@linkcode http://www.example.com/foo} y {@linkplain http://www.example.com/bar} z', @@ -55,7 +55,7 @@ suite('typescript.previewer', () => { 'x [`http://www.example.com/foo`](http://www.example.com/foo) y [http://www.example.com/bar](http://www.example.com/bar) z'); }); - test('Should parse url jsdoc @link in param tag', async () => { + test('Should parse url jsdoc @link in param tag', () => { assert.strictEqual( tagsToMarkdown([ { @@ -66,7 +66,7 @@ suite('typescript.previewer', () => { '*@param* `a` — x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z'); }); - test('Should ignore unclosed jsdocs @link', async () => { + test('Should ignore unclosed jsdocs @link', () => { assert.strictEqual( documentationToMarkdown( 'x {@link http://www.example.com/foo y {@link http://www.example.com/bar bar} z', @@ -76,7 +76,7 @@ suite('typescript.previewer', () => { 'x {@link http://www.example.com/foo y [bar](http://www.example.com/bar) z'); }); - test('Should support non-ascii characters in parameter name (#90108)', async () => { + test('Should support non-ascii characters in parameter name (#90108)', () => { assert.strictEqual( tagsToMarkdown([ { @@ -135,7 +135,35 @@ suite('typescript.previewer', () => { ); }); - test('Should render @linkcode symbol name as code', async () => { + test('Should not render @link inside of @example #187768', () => { + assert.strictEqual( + tagsToMarkdown([ + { + "name": "example", + "text": [ + { + "text": "1 + 1 ", + "kind": "text" + }, + { + "text": "{@link ", + "kind": "link" + }, + { + "text": "foo", + "kind": "linkName" + }, + { + "text": "}", + "kind": "link" + } + ] + } + ], noopToResource), + '*@example* \n```\n1 + 1 {@link foo}\n```'); + }); + + test('Should render @linkcode symbol name as code', () => { assert.strictEqual( asPlainTextWithLinks([ { "text": "a ", "kind": "text" }, @@ -155,7 +183,7 @@ suite('typescript.previewer', () => { 'a [`dog`](command:_typescript.openJsDocLink?%5B%7B%22file%22%3A%7B%22path%22%3A%22%2Fpath%2Ffile.ts%22%2C%22scheme%22%3A%22file%22%7D%2C%22position%22%3A%7B%22line%22%3A6%2C%22character%22%3A4%7D%7D%5D) b'); }); - test('Should render @linkcode text as code', async () => { + test('Should render @linkcode text as code', () => { assert.strictEqual( asPlainTextWithLinks([ { "text": "a ", "kind": "text" }, diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 984356f17b4..86c6bb8d9f1 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -29,6 +29,7 @@ import { PluginManager, TypeScriptServerPlugin } from './tsServer/plugins'; import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './logging/telemetry'; import Tracer from './logging/tracer'; import { ProjectType, inferredProjectCompilerOptions } from './tsconfig'; +import { Schemes } from './configuration/schemes'; export interface TsDiagnostics { @@ -762,6 +763,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType return undefined; } + // For notebook cells, we need to use the notebook document to look up the workspace + if (resource.scheme === Schemes.notebookCell) { + for (const notebook of vscode.workspace.notebookDocuments) { + for (const cell of notebook.getCells()) { + if (cell.document.uri.toString() === resource.toString()) { + resource = notebook.uri; + break; + } + } + } + } + for (const root of roots.sort((a, b) => a.uri.fsPath.length - b.uri.fsPath.length)) { if (root.uri.scheme === resource.scheme && root.uri.authority === resource.authority) { if (resource.fsPath.startsWith(root.uri.fsPath + path.sep)) { @@ -770,7 +783,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } } - return undefined; + return vscode.workspace.getWorkspaceFolder(resource)?.uri; } public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise> { diff --git a/extensions/typescript-language-features/web/webServer.ts b/extensions/typescript-language-features/web/webServer.ts index 6580a84995c..1688a93fcc0 100644 --- a/extensions/typescript-language-features/web/webServer.ts +++ b/extensions/typescript-language-features/web/webServer.ts @@ -76,6 +76,15 @@ function toTsWatcherKind(event: 'create' | 'change' | 'delete') { throw new Error(`Unknown event: ${event}`); } +class AccessOutsideOfRootError extends Error { + constructor( + public readonly filepath: string, + public readonly projectRootPaths: readonly string[] + ) { + super(`Could not read file outside of project root ${filepath}`); + } +} + type ServerHostWithImport = ts.server.ServerHost & { importPlugin(root: string, moduleName: string): Promise }; function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient: ApiClient | undefined, args: string[], fsWatcher: MessagePort): ServerHostWithImport { @@ -455,7 +464,7 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient } if (allowRead === 'block') { - throw new Error(`Could not read file outside of project root ${filepath}`); + throw new AccessOutsideOfRootError(filepath, Array.from(projectRootPaths.keys())); } return uri; diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index a86d12d0e3f..2b8e967e6cb 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -348,6 +348,13 @@ jsonc-parser@^3.2.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -365,15 +372,17 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -semver@5.5.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== +semver@7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" semver@^5.3.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== shimmer@^1.1.0, shimmer@^1.2.0: version "1.2.1" @@ -413,3 +422,8 @@ vscode-uri@3.0.3, vscode-uri@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/vb/package.json b/extensions/vb/package.json index 801ef7180da..6e5dd080c0b 100644 --- a/extensions/vb/package.json +++ b/extensions/vb/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/asp.vb.net.tmbundle Syntaxes/ASP%20VB.net.plist ./syntaxes/asp-vb-net.tmlanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/asp.vb.net.tmbundle Syntaxes/ASP%%20VB.net.plist ./syntaxes/asp-vb-net.tmlanguage.json" }, "contributes": { "languages": [ diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index b230a1a4897..b3e681dc25b 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -44,8 +44,8 @@ "timeline", "tokenInformation", "treeItemCheckbox", + "treeViewActiveItem", "treeViewReveal", - "testInvalidateResults", "workspaceTrust", "telemetry", "windowActivity", diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts index 189bb8e3747..84226f62988 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts @@ -6,12 +6,22 @@ import * as assert from 'assert'; import { basename } from 'path'; import { commands, debug, Disposable, window, workspace } from 'vscode'; -import { assertNoRpc, disposeAll } from '../utils'; +import { assertNoRpc, createRandomFile, disposeAll } from '../utils'; suite('vscode API - debug', function () { teardown(assertNoRpc); + test('breakpoints are available before accessing debug extension API', async () => { + const file = await createRandomFile(undefined, undefined, '.js'); + const doc = await workspace.openTextDocument(file); + await window.showTextDocument(doc); + await commands.executeCommand('editor.debug.action.toggleBreakpoint'); + + assert.strictEqual(debug.breakpoints.length, 1); + await commands.executeCommand('editor.debug.action.toggleBreakpoint'); + }); + test('breakpoints', async function () { assert.strictEqual(debug.breakpoints.length, 0); let onDidChangeBreakpointsCounter = 0; 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 b2a8bb3a9cc..04921231fc8 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 { deepStrictEqual, doesNotThrow, equal, ok, strictEqual, throws } from 'assert'; -import { ConfigurationTarget, Disposable, env, EnvironmentVariableCollection, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EnvironmentVariableScope, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode'; +import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableCollection, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EnvironmentVariableScope, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode'; import { assertNoRpc, poll } from '../utils'; // Disable terminal tests: @@ -347,8 +347,47 @@ import { assertNoRpc, poll } from '../utils'; }); }); + suite('selection', () => { + test('should be undefined immediately after creation', async () => { + const terminal = window.createTerminal({ name: 'selection test' }); + terminal.show(); + equal(terminal.selection, undefined); + terminal.dispose(); + }); + test('should be defined after selecting all content', async () => { + const terminal = window.createTerminal({ name: 'selection test' }); + terminal.show(); + // Wait for some terminal data + await new Promise(r => { + const disposable = window.onDidWriteTerminalData(() => { + disposable.dispose(); + r(); + }); + }); + await commands.executeCommand('workbench.action.terminal.selectAll'); + await poll(() => Promise.resolve(), () => terminal.selection !== undefined, 'selection should be defined'); + terminal.dispose(); + }); + test('should be undefined after clearing a selection', async () => { + const terminal = window.createTerminal({ name: 'selection test' }); + terminal.show(); + // Wait for some terminal data + await new Promise(r => { + const disposable = window.onDidWriteTerminalData(() => { + disposable.dispose(); + r(); + }); + }); + await commands.executeCommand('workbench.action.terminal.selectAll'); + await poll(() => Promise.resolve(), () => terminal.selection !== undefined, 'selection should be defined'); + await commands.executeCommand('workbench.action.terminal.clearSelection'); + await poll(() => Promise.resolve(), () => terminal.selection === undefined, 'selection should not be defined'); + terminal.dispose(); + }); + }); + suite('window.onDidWriteTerminalData', () => { - test('should listen to all future terminal data events', (done) => { + test.skip('should listen to all future terminal data events', (done) => { const openEvents: string[] = []; const dataEvents: { name: string; data: string }[] = []; const closeEvents: string[] = []; @@ -897,6 +936,7 @@ import { assertNoRpc, poll } from '../utils'; { value: 'scoped~b2~', type: EnvironmentVariableMutatorType.Append, options: defaultOptions }, { value: 'scoped~c2~', type: EnvironmentVariableMutatorType.Prepend, options: defaultOptions } ]); + deepStrictEqual(entries.map(v => v[0]), ['A', 'B', 'C']); }); }); }); diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.code-snippets b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.code-snippets new file mode 100644 index 00000000000..fb3df23dd42 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.code-snippets @@ -0,0 +1,40 @@ +{ + // Each snippet is defined under a snippet name and has a scope, prefix, body and + // description. The scope defines in watch languages the snippet is applicable. The prefix is what is + // used to trigger the snippet and the body will be expanded and inserted.Possible variables are: + // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. + // Placeholders with the same ids are connected. + // Example: + "MSFT Copyright Header": { + "scope": "javascript,typescript,css", + "prefix": [ + "header", + "stub", + "copyright" + ], + "body": [ + "/*---------------------------------------------------------------------------------------------", + " * Copyright (c) Microsoft Corporation. All rights reserved.", + " * Licensed under the MIT License. See License.txt in the project root for license information.", + " *--------------------------------------------------------------------------------------------*/", + "", + "$0" + ], + "description": "Insert Copyright Statement" + }, + "TS -> Inject Service": { + "scope": "typescript", + "description": "Constructor Injection Pattern", + "prefix": "@inject", + "body": "@$1 private readonly _$2: ${1},$0" + }, + "TS -> Event & Emitter": { + "scope": "typescript", + "prefix": "emitter", + "description": "Add emitter and event properties", + "body": [ + "private readonly _onDid$1 = new Emitter<$2>();", + "readonly onDid$1: Event<$2> = this._onDid$1.event;" + ], + } +} diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_code-snippets.json b/extensions/vscode-colorize-tests/test/colorize-results/test_code-snippets.json new file mode 100644 index 00000000000..5403d20a7d8 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_code-snippets.json @@ -0,0 +1,3460 @@ +[ + { + "c": "{", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "//", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js punctuation.definition.comment.json.comments.snippets", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " Each snippet is defined under a snippet name and has a scope, prefix, body and", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "//", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js punctuation.definition.comment.json.comments.snippets", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " description. The scope defines in watch languages the snippet is applicable. The prefix is what is", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "//", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js punctuation.definition.comment.json.comments.snippets", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " used to trigger the snippet and the body will be expanded and inserted.Possible variables are:", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "//", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js punctuation.definition.comment.json.comments.snippets", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "//", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js punctuation.definition.comment.json.comments.snippets", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " Placeholders with the same ids are connected.", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "//", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js punctuation.definition.comment.json.comments.snippets", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " Example:", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets comment.line.double-slash.js", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "MSFT Copyright Header", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "{", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "scope", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "javascript,typescript,css", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "prefix", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "[", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.definition.array.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "header", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "stub", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "copyright", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "]", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.definition.array.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "body", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "[", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.definition.array.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "/*---------------------------------------------------------------------------------------------", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": " * Copyright (c) Microsoft Corporation. All rights reserved.", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": " * Licensed under the MIT License. See License.txt in the project root for license information.", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": " *--------------------------------------------------------------------------------------------*/", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "0", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "]", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.definition.array.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "description", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "Insert Copyright Statement", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "}", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "TS -> Inject Service", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "{", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "scope", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "typescript", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "description", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "Constructor Injection Pattern", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "prefix", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "@inject", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "body", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "@", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "1", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": " private readonly _", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "2", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": ": ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.brackets.json.comments.snippets meta.insertion.tabstop.bracket.json.comments.snippets punctuation.section.insertion.dollar.brackets.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.brackets.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "{", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.brackets.json.comments.snippets meta.insertion.tabstop.bracket.json.comments.snippets punctuation.section.insertion.bracket.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.bracket.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "1", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.brackets.json.comments.snippets meta.insertion.tabstop.bracket.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "}", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.brackets.json.comments.snippets meta.insertion.tabstop.bracket.json.comments.snippets punctuation.section.insertion.bracket.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.bracket.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "0", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "}", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "TS -> Event & Emitter", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "{", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "scope", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "typescript", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "prefix", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "emitter", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "description", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.end.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "Add emitter and event properties", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "body", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets string.json.comments.snippets support.type.property-name.json.comments.snippets punctuation.support.type.property-name.begin.json.comments.snippets", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name.json: #0451A5", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name.json: #0451A5", + "hc_black": "support.type.property-name: #D4D4D4", + "dark_modern": "support.type.property-name: #9CDCFE", + "hc_light": "support.type.property-name.json: #0451A5", + "light_modern": "support.type.property-name.json: #0451A5" + } + }, + { + "c": ":", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.key-value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "[", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.definition.array.begin.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "private readonly _onDid", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "1", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": " = new Emitter<", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "2", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": ">();", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.separator.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.begin.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "readonly onDid", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "1", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": ": Event<", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "2", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "> = this._onDid", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets punctuation.section.insertion.dollar.simple.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.punctuation.section.insertion.dollar.simple.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": "1", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets meta.insertion.simple.numeric.json.comments.snippets meta.insertion.tabstop.simple.json.comments.snippets variable.other.normal.json.comments.snippets keyword.operator.insertion.json.comments.snippets custom.variable.other.normal.numeric.json.comments.snippets", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "dark_modern": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000", + "light_modern": "keyword.operator: #000000" + } + }, + { + "c": ".event;", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets meta.any.json.comments.snippets string.quoted.double.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\"", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets string.quoted.double.json.comments.snippets punctuation.definition.string.end.json.comments.snippets", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" + } + }, + { + "c": "\t\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "]", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.array.json.comments.snippets punctuation.definition.array.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": ",", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets punctuation.separator.dictionary.pair.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "\t", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "}", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets meta.structure.dictionary.value.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "}", + "t": "source.json.comments.snippets meta.structure.dictionary.json.comments.snippets punctuation.definition.dictionary.end.json.comments.snippets", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + } +] \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_md.json b/extensions/vscode-colorize-tests/test/colorize-results/test_md.json index d28d9c74747..648feab845a 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_md.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_md.json @@ -2842,8 +2842,8 @@ } }, { - "c": "| Header | Header | Right |", - "t": "text.html.markdown meta.paragraph.markdown", + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2856,8 +2856,8 @@ } }, { - "c": "| ------ | ------ | -----: |", - "t": "text.html.markdown meta.paragraph.markdown", + "c": " Header ", + "t": "text.html.markdown markup.table.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2870,8 +2870,8 @@ } }, { - "c": "| Cell | Cell | $10 |", - "t": "text.html.markdown meta.paragraph.markdown", + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2884,8 +2884,428 @@ } }, { - "c": "| Cell | Cell | $20 |", - "t": "text.html.markdown meta.paragraph.markdown", + "c": " Header ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " Right ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "------", + "t": "text.html.markdown markup.table.markdown punctuation.separator.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "------", + "t": "text.html.markdown markup.table.markdown punctuation.separator.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "-----:", + "t": "text.html.markdown markup.table.markdown punctuation.separator.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " Cell ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " Cell ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " $10 ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " Cell ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " Cell ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " $20 ", + "t": "text.html.markdown markup.table.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "|", + "t": "text.html.markdown markup.table.markdown punctuation.definition.table.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_py.json b/extensions/vscode-colorize-tests/test/colorize-results/test_py.json index 9d40766533d..e858cd5f201 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_py.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_py.json @@ -395,42 +395,42 @@ "c": "'''", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.begin.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "Make the monkey eat N bananas!", "t": "source.python string.quoted.docstring.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "'''", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.end.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -3181,42 +3181,42 @@ "c": "\"\"\"", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.begin.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "Berechnung der zu zahlenden Steuern fuer ein zu versteuerndes Einkommen von x", "t": "source.python string.quoted.docstring.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "\"\"\"", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.end.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7423,56 +7423,56 @@ "c": "'''", "t": "source.python string.quoted.docstring.raw.multi.python punctuation.definition.string.begin.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "Module docstring", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " Some text followed by code sample:", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " ", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7493,28 +7493,28 @@ "c": "for a in foo(2, b=1,", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " ", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7535,28 +7535,28 @@ "c": " c=3):", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " ", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7577,56 +7577,56 @@ "c": " print(a)", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " 0", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " 1", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "'''", "t": "source.python string.quoted.docstring.raw.multi.python punctuation.definition.string.end.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } } ] \ No newline at end of file diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 50070eb668f..5b00e9e5431 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -228,10 +228,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -typescript@5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" - integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== +typescript@^5.2.0-dev.20230731: + version "5.2.0-dev.20230731" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230731.tgz#a72083c07043568ab856dd8ca0d8f3a708c3e3a6" + integrity sha512-RJVLgnDgu67ZrohYy0aBea+5TICfRod36+24zw0bR/KJDQJO9mlIjTC0k+/PKw87fXP5JuUHqepEk15PvFya7A== vscode-grammar-updater@^1.1.0: version "1.1.0" diff --git a/package.json b/package.json index 566a3838b20..d1bed1250db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.80.0", - "distro": "4ec8f427013e8d904920b6bdd931e78344401c19", + "version": "1.82.0", + "distro": "bc3b9188018c76ecdd69d813c95c5101e5f3da5e", "author": { "name": "Microsoft Corporation" }, @@ -70,7 +70,7 @@ "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.4", - "@vscode/proxy-agent": "^0.14.1", + "@vscode/proxy-agent": "^0.17.2", "@vscode/ripgrep": "^1.15.5", "@vscode/spdlog": "^0.13.10", "@vscode/sqlite3": "5.1.6-vscode", @@ -79,13 +79,14 @@ "@vscode/windows-mutex": "^0.4.4", "@vscode/windows-process-tree": "^0.5.0", "@vscode/windows-registry": "^1.1.0", - "graceful-fs": "4.2.8", + "graceful-fs": "4.2.11", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", "jschardet": "3.0.0", + "kerberos": "^2.0.1", "keytar": "7.9.0", "minimist": "^1.2.6", - "native-is-elevated": "0.6.0", + "native-is-elevated": "0.7.0", "native-keymap": "^3.3.2", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta1", @@ -94,19 +95,18 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.3", - "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.1", - "xterm-addon-search": "0.13.0-beta.2", - "xterm-addon-serialize": "0.11.0-beta.2", + "xterm": "5.3.0-beta.21", + "xterm-addon-canvas": "0.5.0-beta.7", + "xterm-addon-image": "0.4.3", + "xterm-addon-search": "0.13.0-beta.4", + "xterm-addon-serialize": "0.11.0-beta.6", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.2", - "xterm-headless": "5.3.0-beta.3", + "xterm-addon-webgl": "0.16.0-beta.6", + "xterm-headless": "5.3.0-beta.21", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, "devDependencies": { - "7zip": "0.0.6", "@playwright/test": "^1.34.3", "@swc/cli": "0.1.62", "@swc/core": "1.3.62", @@ -117,6 +117,7 @@ "@types/gulp-postcss": "^8.0.0", "@types/gulp-svgmin": "^1.2.1", "@types/http-proxy-agent": "^2.0.1", + "@types/kerberos": "^1.1.2", "@types/keytar": "^4.4.0", "@types/minimist": "^1.2.1", "@types/mocha": "^9.1.1", @@ -149,7 +150,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "22.3.14", + "electron": "22.3.18", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", @@ -211,7 +212,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.2.0-dev.20230621", + "typescript": "^5.2.0-dev.20230731", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/product.json b/product.json index 5c83e0ff30d..2ae3ee8f0f8 100644 --- a/product.json +++ b/product.json @@ -36,8 +36,8 @@ "builtInExtensions": [ { "name": "ms-vscode.js-debug-companion", - "version": "1.1.1", - "sha256": "095dc6d5d45490966dfa74d24add92036b42bf02a37d88c4b0f510dc91deeee6", + "version": "1.1.2", + "sha256": "e034b8b41beb4e97e02c70f7175bd88abe66048374c2bd629f54bb33354bc2aa", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", @@ -52,8 +52,8 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.80.0", - "sha256": "ac75e4ecf79efafa2bb0e6c8c548a6c8359555623f677a386cbcea80630aee4f", + "version": "1.81.0", + "sha256": "6d1c7ee89881afd65e8fee47445b6a1c5fb345bf30e2bdf70cd2fdd8d1ff6dec", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", diff --git a/remote/package.json b/remote/package.json index bb3ae0f3c5c..03295dbb0e0 100644 --- a/remote/package.json +++ b/remote/package.json @@ -7,17 +7,18 @@ "@microsoft/1ds-post-js": "^3.2.2", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.14.1", + "@vscode/proxy-agent": "^0.17.2", "@vscode/ripgrep": "^1.15.5", "@vscode/spdlog": "^0.13.10", "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-process-tree": "^0.5.0", "@vscode/windows-registry": "^1.1.0", "cookie": "^0.4.0", - "graceful-fs": "4.2.8", + "graceful-fs": "4.2.11", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", "jschardet": "3.0.0", + "kerberos": "^2.0.1", "keytar": "7.9.0", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", @@ -26,14 +27,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.3", - "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.1", - "xterm-addon-search": "0.13.0-beta.2", - "xterm-addon-serialize": "0.11.0-beta.2", + "xterm": "5.3.0-beta.21", + "xterm-addon-canvas": "0.5.0-beta.7", + "xterm-addon-image": "0.4.3", + "xterm-addon-search": "0.13.0-beta.4", + "xterm-addon-serialize": "0.11.0-beta.6", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.2", - "xterm-headless": "5.3.0-beta.3", + "xterm-addon-webgl": "0.16.0-beta.6", + "xterm-headless": "5.3.0-beta.21", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index db7baab82d3..ff0ed3155d9 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.3", - "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.1", - "xterm-addon-search": "0.13.0-beta.2", + "xterm": "5.3.0-beta.21", + "xterm-addon-canvas": "0.5.0-beta.7", + "xterm-addon-image": "0.4.3", + "xterm-addon-search": "0.13.0-beta.4", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.2" + "xterm-addon-webgl": "0.16.0-beta.6" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 2f879c22bf6..eaba3881931 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -68,32 +68,32 @@ vscode-textmate@9.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== -xterm-addon-canvas@0.5.0-beta.2: - version "0.5.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4" - integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A== +xterm-addon-canvas@0.5.0-beta.7: + version "0.5.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.7.tgz#27a6753a0d8551248bffb15c29d5d88bf6b2a071" + integrity sha512-33a8kSR/uirSrtPkNiCO5MnRwuvR0hTvm1K+541XDsa9QS0VF0xPZoC4MEWHHXfSMzrTFgx1edpXqYOKc2yWtw== -xterm-addon-image@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a" - integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w== +xterm-addon-image@0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.3.tgz#b9e563d908c6aa82458852eb5b8a7ea931c19dd3" + integrity sha512-OyNs5qpW884CXJGRdqOnaiH2L9HLNyfO4oCHOH9mCVFjp099Aqc4u/DBs4nyTPcBgdeKrlE8xzI1i6y8H94aCw== -xterm-addon-search@0.13.0-beta.2: - version "0.13.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.2.tgz#c984a35312acad4ce768d17bc49adffa90eece61" - integrity sha512-+VoPhIRmfiX2uh2t6xD/RJtBYjVjrkNa3dKQnOYEp4UbYzDjK57rZX652mnZ82TQfk/juxf7v+jV5aRdNLZVbA== +xterm-addon-search@0.13.0-beta.4: + version "0.13.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.4.tgz#a9817600cc0a71dee524d0f025e88e013f87f14a" + integrity sha512-A9Ps7iqGVUSphLCVrGYrfua1ADjtm9ff5CLs8cpiZnMK45X1FTIrXxEWvFccoiwtmZINEDJPGvbfIIqu8ZkXyg== xterm-addon-unicode11@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.2: - version "0.16.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8" - integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg== +xterm-addon-webgl@0.16.0-beta.6: + version "0.16.0-beta.6" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.6.tgz#5f8a598081a117dec15b4ead8160bf8e972fbfd0" + integrity sha512-bRFahQyVuiRmeakLzTrs3V3KSWBDOcePfT/Q7dwWfh8NPpilg6/Zm04ijq99Nsg1AYbfNWh+P05Myppa8uimCA== -xterm@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644" - integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w== +xterm@5.3.0-beta.21: + version "5.3.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.21.tgz#6b19160e8442bdc0a505213a4aa1c6a231af9deb" + integrity sha512-AtkHnXSRbQ+uKgpYyQ4fkQRMur7rzM7kLY5hKcYZ8R8OpQmsbQxahStRNVBPP32rk+uKOmuZIQ61eQR7y44tGQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index 91363ef48b6..5a0410701ec 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -48,27 +48,27 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@tootallnate/once@1", "@tootallnate/once@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-3.0.0.tgz#d52238c9052d746c9689523e650160e70786bc9a" + integrity sha512-OAdBVB7rlwvLD+DiecSAyVKzKVmSfXbouCyM5I6wHGi4MGXIyFqErg1IvyJ7PI1e+GYZuZh7cCHV/c4LA8SKMw== "@vscode/iconv-lite-umd@0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== -"@vscode/proxy-agent@^0.14.1": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.14.1.tgz#61866221a8fbd5143f73a14c29deccdf85f13113" - integrity sha512-bJxCO9E6zDpy90TiViAZgFjAgo83gS0Lh5CUIu/JZ8p5UwwQ37Y6LZH2f2l6kBr2RGdNSRbORTFrfmLtr1faRA== +"@vscode/proxy-agent@^0.17.2": + version "0.17.2" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.2.tgz#0e0dac24478e2d71a4fd1b2bb5f84dc61add79e2" + integrity sha512-aKRo1YfUCsgEjHvr2HXfM6dwHhieyO6G+WHly7jewyyTJ1nANWEocS3JRnRbC4KjlajKhSUEOx838cdnY/vRtA== dependencies: - "@tootallnate/once" "^1.1.2" - agent-base "^6.0.2" - debug "^4.3.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - socks-proxy-agent "^5.0.0" + "@tootallnate/once" "^3.0.0" + agent-base "^7.0.1" + debug "^4.3.4" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + socks-proxy-agent "^8.0.1" optionalDependencies: "@vscode/windows-ca-certs" "^0.3.1" @@ -120,7 +120,7 @@ agent-base@4: dependencies: es6-promisify "^5.0.0" -agent-base@6, agent-base@^6.0.2: +agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -134,6 +134,13 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + dependencies: + debug "^4.3.4" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -237,10 +244,10 @@ debug@4: dependencies: ms "^2.1.1" -debug@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" @@ -338,10 +345,10 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -graceful-fs@4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@4.2.11: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-unicode@^2.0.0: version "2.0.1" @@ -356,14 +363,13 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== +http-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" + integrity sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ== dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" + agent-base "^7.1.0" + debug "^4.3.4" https-proxy-agent@^2.2.3: version "2.2.4" @@ -381,6 +387,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" + integrity sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ== + dependencies: + agent-base "^7.0.2" + debug "4" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -396,10 +410,10 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== is-extglob@^2.1.1: version "2.1.1" @@ -440,6 +454,15 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +kerberos@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5" + integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw== + dependencies: + bindings "^1.5.0" + node-addon-api "^4.3.0" + prebuild-install "7.1.1" + keytar@7.9.0: version "7.9.0" resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" @@ -581,6 +604,24 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +prebuild-install@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prebuild-install@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" @@ -661,9 +702,9 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -691,27 +732,27 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -smart-buffer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" - integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -socks-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" - integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== +socks-proxy-agent@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" + integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== dependencies: - agent-base "6" - debug "4" - socks "^2.3.3" + agent-base "^7.0.1" + debug "^4.3.4" + socks "^2.7.1" -socks@^2.3.3: - version "2.6.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" - integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== +socks@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== dependencies: - ip "^1.1.5" - smart-buffer "^4.1.0" + ip "^2.0.0" + smart-buffer "^4.2.0" string-width@^1.0.1: version "1.0.2" @@ -836,45 +877,45 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-canvas@0.5.0-beta.2: - version "0.5.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4" - integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A== +xterm-addon-canvas@0.5.0-beta.7: + version "0.5.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.7.tgz#27a6753a0d8551248bffb15c29d5d88bf6b2a071" + integrity sha512-33a8kSR/uirSrtPkNiCO5MnRwuvR0hTvm1K+541XDsa9QS0VF0xPZoC4MEWHHXfSMzrTFgx1edpXqYOKc2yWtw== -xterm-addon-image@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a" - integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w== +xterm-addon-image@0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.3.tgz#b9e563d908c6aa82458852eb5b8a7ea931c19dd3" + integrity sha512-OyNs5qpW884CXJGRdqOnaiH2L9HLNyfO4oCHOH9mCVFjp099Aqc4u/DBs4nyTPcBgdeKrlE8xzI1i6y8H94aCw== -xterm-addon-search@0.13.0-beta.2: - version "0.13.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.2.tgz#c984a35312acad4ce768d17bc49adffa90eece61" - integrity sha512-+VoPhIRmfiX2uh2t6xD/RJtBYjVjrkNa3dKQnOYEp4UbYzDjK57rZX652mnZ82TQfk/juxf7v+jV5aRdNLZVbA== +xterm-addon-search@0.13.0-beta.4: + version "0.13.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.4.tgz#a9817600cc0a71dee524d0f025e88e013f87f14a" + integrity sha512-A9Ps7iqGVUSphLCVrGYrfua1ADjtm9ff5CLs8cpiZnMK45X1FTIrXxEWvFccoiwtmZINEDJPGvbfIIqu8ZkXyg== -xterm-addon-serialize@0.11.0-beta.2: - version "0.11.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.2.tgz#fff924decfbf1bc08434317894f985fef7bb260b" - integrity sha512-tN4IT2e+EIpsoFpMONUh1OAuoVAcV7AYOLsqMKgH6GNWB1D/LKGo3cwjpw1vwRZzDJJcCcLxYgxlUzhPbDbLxQ== +xterm-addon-serialize@0.11.0-beta.6: + version "0.11.0-beta.6" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.6.tgz#2533a2f6bf41327cfcc51a92fca79404499cffe2" + integrity sha512-wXx3j9Nf+JOxoBW8iZElEPVEBD6LRDlnrGhfe4oSEa+wDJX+8iORcIKH06mNBlwSApajawrDhpcugwAhjvSOHg== xterm-addon-unicode11@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.2: - version "0.16.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8" - integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg== +xterm-addon-webgl@0.16.0-beta.6: + version "0.16.0-beta.6" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.6.tgz#5f8a598081a117dec15b4ead8160bf8e972fbfd0" + integrity sha512-bRFahQyVuiRmeakLzTrs3V3KSWBDOcePfT/Q7dwWfh8NPpilg6/Zm04ijq99Nsg1AYbfNWh+P05Myppa8uimCA== -xterm-headless@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.3.tgz#153cf330082f4b2aae64ff736ef0b62d93c30da8" - integrity sha512-4i/bpFoAn4D4ZA4g8RKrJdhq2EcB1HN2E25yUg3omRbWCOZ2Gp9nAn+62LYzX5rvGqdNbpUTRJLX0lKwEFyLFw== +xterm-headless@5.3.0-beta.21: + version "5.3.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.21.tgz#3d41388c25c07530ecaeb081546e3e660d707f5d" + integrity sha512-hCwZHS13NU9bNZAE8BK5FDERjkLGXwDUh1M/Zz46S8WB/AtSRN2kt0lIES3IzZdEOzUjPdfiI4UNGXiLIpLZ/w== -xterm@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644" - integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w== +xterm@5.3.0-beta.21: + version "5.3.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.21.tgz#6b19160e8442bdc0a505213a4aa1c6a231af9deb" + integrity sha512-AtkHnXSRbQ+uKgpYyQ4fkQRMur7rzM7kLY5hKcYZ8R8OpQmsbQxahStRNVBPP32rk+uKOmuZIQ61eQR7y44tGQ== yallist@^4.0.0: version "4.0.0" diff --git a/scripts/playground-server.ts b/scripts/playground-server.ts index 9468087409f..1c2074ee191 100644 --- a/scripts/playground-server.ts +++ b/scripts/playground-server.ts @@ -280,7 +280,7 @@ function makeLoaderJsHotReloadable(loaderJsCode: string, fileChangesUrl: URL): s if (___globalModuleManager._modules2[moduleId]) { const srcUrl = ___globalModuleManager._config.moduleIdToPaths(data.moduleId); const newSrc = await (await fetch(srcUrl)).text(); - (new Function('define', newSrc))(function (deps, callback) { + (new Function('define', newSrc))(function (deps, callback) { // CodeQL [SM01632] This code is only executed during development (as part of the dev-only playground-server). It is required for the hot-reload functionality. const oldModule = ___globalModuleManager._modules2[moduleId]; delete ___globalModuleManager._modules2[moduleId]; diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 778f1deb9a6..66da7fb0e41 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -88,9 +88,6 @@ window['MonacoEnvironment'] = {}; - // VSCODE_GLOBALS: node_modules - globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target, mod) => (require.__$__nodeRequire ?? require)(String(mod)) }); - const loaderConfig = { baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`, 'vs/nls': nlsConfig, diff --git a/src/bootstrap.js b/src/bootstrap.js index 0617532bd58..bd0e92e672c 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -23,6 +23,7 @@ const Module = typeof require === 'function' ? require('module') : undefined; const path = typeof require === 'function' ? require('path') : undefined; const fs = typeof require === 'function' ? require('fs') : undefined; + const util = typeof require === 'function' ? require('util') : undefined; //#region global bootstrapping @@ -222,8 +223,8 @@ return ipcRenderer.invoke('vscode:readNlsFile', ...pathSegments); } - if (fs && path) { - return (await fs.promises.readFile(path.join(...pathSegments))).toString(); + if (fs && path && util) { + return (await util.promisify(fs.readFile)(path.join(...pathSegments))).toString(); } throw new Error('Unsupported operation (read NLS files)'); @@ -240,8 +241,8 @@ return ipcRenderer.invoke('vscode:writeNlsFile', path, content); } - if (fs) { - return fs.promises.writeFile(path, content); + if (fs && util) { + return util.promisify(fs.writeFile)(path, content); } throw new Error('Unsupported operation (write NLS files)'); diff --git a/src/buildfile.js b/src/buildfile.js index 3f384d2a082..f03de33fb3d 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -5,7 +5,7 @@ /** * @param {string} name - * @param {string[]} exclude + * @param {string[]=} exclude */ function createModuleDescription(name, exclude) { @@ -53,7 +53,7 @@ exports.workerProfileAnalysis = [createEditorWorkerModuleDescription('vs/platfor exports.workbenchDesktop = [ createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer'), - createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/worker/textMate.worker'), + createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker'), createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), createModuleDescription('vs/platform/files/node/watcher/watcherMain'), createModuleDescription('vs/platform/terminal/node/ptyHostMain'), @@ -62,7 +62,7 @@ exports.workbenchDesktop = [ exports.workbenchWeb = [ createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer'), - createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/worker/textMate.worker'), + createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker'), createModuleDescription('vs/code/browser/workbench/workbench', ['vs/workbench/workbench.web.main']) ]; diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index a309a50adaa..c0a2e174591 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -17,29 +17,10 @@ "./vs/*" ] }, + "target": "es2022", + "useDefineForClassFields": false, "lib": [ - "ES2016", - "ES2017.Object", - "ES2017.String", - "ES2017.Intl", - "ES2017.TypedArrays", - "ES2018.AsyncIterable", - "ES2018.AsyncGenerator", - "ES2018.Promise", - "ES2018.Regexp", - "ES2018.Intl", - "ES2019.Array", - "ES2019.Object", - "ES2019.String", - "ES2019.Symbol", - "ES2020.BigInt", - "ES2020.Promise", - "ES2020.String", - "ES2020.Symbol.WellKnown", - "ES2020.Intl", - "ES2021.Promise", - "ES2021.String", - "ES2021.WeakRef", + "ES2022", "DOM", "DOM.Iterable", "WebWorker.ImportScripts" diff --git a/src/tsconfig.json b/src/tsconfig.json index 5587571ab70..df96ed71247 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -7,7 +7,6 @@ "allowJs": true, "resolveJsonModule": true, "outDir": "../out/vs", - "target": "es2021", "types": [ "keytar", "mocha", diff --git a/src/tsec.exemptions.json b/src/tsec.exemptions.json index 1423e7cc4e6..eb8405c96cb 100644 --- a/src/tsec.exemptions.json +++ b/src/tsec.exemptions.json @@ -14,6 +14,7 @@ "vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts" ], "ban-trustedtypes-createpolicy": [ + "vs/amdX.ts", "vs/base/browser/trustedTypes.ts", "vs/base/worker/workerMain.ts", "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts" @@ -23,6 +24,7 @@ "vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts" ], "ban-worker-importscripts": [ + "vs/amdX.ts", "vs/workbench/services/extensions/worker/polyfillNestedWorker.ts", "vs/workbench/api/worker/extensionHostWorker.ts", "vs/base/worker/workerMain.ts" diff --git a/src/typings/vscode-globals-modules.d.ts b/src/typings/vscode-globals-modules.d.ts index 443c2b687db..c538b99b63f 100644 --- a/src/typings/vscode-globals-modules.d.ts +++ b/src/typings/vscode-globals-modules.d.ts @@ -18,6 +18,8 @@ declare global { net: typeof import('net'); os: typeof import('os'); module: typeof import('module'); + fs: typeof import('fs'), + vm: typeof import('vm'), ['native-watchdog']: typeof import('native-watchdog') perf_hooks: typeof import('perf_hooks'); diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts new file mode 100644 index 00000000000..75b21c60828 --- /dev/null +++ b/src/vs/amdX.ts @@ -0,0 +1,208 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isESM } from 'vs/base/common/amd'; +import { AppResourcePath, FileAccess, nodeModulesAsarPath, nodeModulesPath } from 'vs/base/common/network'; +import * as platform from 'vs/base/common/platform'; +import { IProductConfiguration } from 'vs/base/common/product'; +import { URI } from 'vs/base/common/uri'; + + +class DefineCall { + constructor( + public readonly id: string | null | undefined, + public readonly dependencies: string[] | null | undefined, + public readonly callback: any + ) { } +} + +class AMDModuleImporter { + public static INSTANCE = new AMDModuleImporter(); + + private readonly _isWebWorker = (typeof self === 'object' && self.constructor && self.constructor.name === 'DedicatedWorkerGlobalScope'); + private readonly _isRenderer = typeof document === 'object'; + + private readonly _defineCalls: DefineCall[] = []; + private _initialized = false; + private _amdPolicy: Pick, 'name' | 'createScriptURL'> | undefined; + + constructor() { } + + private _initialize(): void { + if (this._initialized) { + return; + } + this._initialized = true; + + (globalThis).define = (id: any, dependencies: any, callback: any) => { + if (typeof id !== 'string') { + callback = dependencies; + dependencies = id; + id = null; + } + if (typeof dependencies !== 'object' || !Array.isArray(dependencies)) { + callback = dependencies; + dependencies = null; + } + // if (!dependencies) { + // dependencies = ['require', 'exports', 'module']; + // } + this._defineCalls.push(new DefineCall(id, dependencies, callback)); + }; + + (globalThis).define.amd = true; + + if (this._isRenderer) { + this._amdPolicy = window.trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value) { + if (value.startsWith(window.location.origin)) { + return value; + } + if (value.startsWith('vscode-file://vscode-app')) { + return value; + } + throw new Error(`[trusted_script_src] Invalid script url: ${value}`); + } + }); + } else if (this._isWebWorker) { + this._amdPolicy = (globalThis).trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value: string) { + return value; + } + }); + } + } + + public async load(scriptSrc: string): Promise { + this._initialize(); + const defineCall = await (this._isWebWorker ? this._workerLoadScript(scriptSrc) : this._isRenderer ? this._rendererLoadScript(scriptSrc) : this._nodeJSLoadScript(scriptSrc)); + if (!defineCall) { + throw new Error(`Did not receive a define call from script ${scriptSrc}`); + } + // TODO require, exports, module + if (Array.isArray(defineCall.dependencies) && defineCall.dependencies.length > 0) { + throw new Error(`Cannot resolve dependencies for script ${scriptSrc}. The dependencies are: ${defineCall.dependencies.join(', ')}`); + } + if (typeof defineCall.callback === 'function') { + return defineCall.callback([]); + } else { + return defineCall.callback; + } + } + + private _rendererLoadScript(scriptSrc: string): Promise { + return new Promise((resolve, reject) => { + const scriptElement = document.createElement('script'); + scriptElement.setAttribute('async', 'async'); + scriptElement.setAttribute('type', 'text/javascript'); + + const unbind = () => { + scriptElement.removeEventListener('load', loadEventListener); + scriptElement.removeEventListener('error', errorEventListener); + }; + + const loadEventListener = (e: any) => { + unbind(); + resolve(this._defineCalls.pop()); + }; + + const errorEventListener = (e: any) => { + unbind(); + reject(e); + }; + + scriptElement.addEventListener('load', loadEventListener); + scriptElement.addEventListener('error', errorEventListener); + if (this._amdPolicy) { + scriptSrc = this._amdPolicy.createScriptURL(scriptSrc) as any as string; + } + scriptElement.setAttribute('src', scriptSrc); + document.getElementsByTagName('head')[0].appendChild(scriptElement); + }); + } + + private _workerLoadScript(scriptSrc: string): Promise { + return new Promise((resolve, reject) => { + try { + if (this._amdPolicy) { + scriptSrc = this._amdPolicy.createScriptURL(scriptSrc) as any as string; + } + importScripts(scriptSrc); + resolve(this._defineCalls.pop()); + } catch (err) { + reject(err); + } + }); + } + + private async _nodeJSLoadScript(scriptSrc: string): Promise { + try { + const fs = globalThis._VSCODE_NODE_MODULES['fs']; + const vm = globalThis._VSCODE_NODE_MODULES['vm']; + const module = globalThis._VSCODE_NODE_MODULES['module']; + + const filePath = URI.parse(scriptSrc).fsPath; + const content = fs.readFileSync(filePath).toString(); + const scriptSource = module.wrap(content.replace(/^#!.*/, '')); + const script = new vm.Script(scriptSource); + const compileWrapper = script.runInThisContext(); + compileWrapper.apply(); + return this._defineCalls.pop(); + + } catch (error) { + throw error; + } + } +} + +const cache = new Map>(); + +let _paths: Record = {}; +if (typeof globalThis.require === 'object') { + _paths = (>globalThis.require).paths ?? {}; +} + +/** + * Utility for importing an AMD node module. This util supports AMD and ESM contexts and should be used while the ESM adoption + * is on its way. + * + * e.g. pass in `vscode-textmate/release/main.js` + */ +export async function importAMDNodeModule(nodeModuleName: string, pathInsideNodeModule: string, isBuilt?: boolean): Promise { + if (isESM) { + + if (isBuilt === undefined) { + const product = globalThis._VSCODE_PRODUCT_JSON as unknown as IProductConfiguration; + isBuilt = Boolean((product ?? (globalThis).vscode?.context?.configuration()?.product)?.commit); + } + + if (_paths[nodeModuleName]) { + nodeModuleName = _paths[nodeModuleName]; + } + + const nodeModulePath = `${nodeModuleName}/${pathInsideNodeModule}`; + if (cache.has(nodeModulePath)) { + return cache.get(nodeModulePath)!; + } + let scriptSrc: string; + if (/^\w[\w\d+.-]*:\/\//.test(nodeModulePath)) { + // looks like a URL + // bit of a special case for: src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts + scriptSrc = nodeModulePath; + } else { + const useASAR = (isBuilt && !platform.isWeb); + const actualNodeModulesPath = (useASAR ? nodeModulesAsarPath : nodeModulesPath); + const resourcePath: AppResourcePath = `${actualNodeModulesPath}/${nodeModulePath}`; + scriptSrc = FileAccess.asBrowserUri(resourcePath).toString(true); + } + const result = AMDModuleImporter.INSTANCE.load(scriptSrc); + cache.set(nodeModulePath, result); + return result; + } else { + return await import(nodeModuleName); + } +} diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 4194d976db2..2ac4671e497 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -16,7 +16,7 @@ class WindowManager { public getZoomLevel(): number { return this._zoomLevel; } - public setZoomLevel(zoomLevel: number, isTrusted: boolean): void { + public setZoomLevel(zoomLevel: number): void { if (this._zoomLevel === zoomLevel) { return; } @@ -159,8 +159,8 @@ export function addMatchMediaChangeListener(query: string | MediaQueryList, call export const PixelRatio = new PixelRatioFacade(); /** A zoom index, e.g. 1, 2, 3 */ -export function setZoomLevel(zoomLevel: number, isTrusted: boolean): void { - WindowManager.INSTANCE.setZoomLevel(zoomLevel, isTrusted); +export function setZoomLevel(zoomLevel: number): void { + WindowManager.INSTANCE.setZoomLevel(zoomLevel); } export function getZoomLevel(): number { return WindowManager.INSTANCE.getZoomLevel(); diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 8f67c230306..3ab23f5bc9f 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { AnchorAlignment, AnchorAxisAlignment, IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAction, IActionRunner } from 'vs/base/common/actions'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; +import { OmitOptional } from 'vs/base/common/types'; export interface IContextMenuEvent { readonly shiftKey?: boolean; @@ -16,8 +18,21 @@ export interface IContextMenuEvent { readonly metaKey?: boolean; } +/** + * A specific context menu location to position the menu at. + * Uses some TypeScript type tricks to prevent allowing to + * pass in a `MouseEvent` and force people to use `StandardMouseEvent`. + */ +type ContextMenuLocation = OmitOptional & { getModifierState?: never }; + export interface IContextMenuDelegate { - getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number }; + /** + * The anchor where to position the context view. + * Use a `HTMLElement` to position the view at the element, + * a `StandardMouseEvent` to position it at the mouse position + * or an `ContextMenuLocation` to position it at a specific location. + */ + getAnchor(): HTMLElement | StandardMouseEvent | ContextMenuLocation; getActions(): readonly IAction[]; getCheckedActionsRepresentation?(action: IAction): 'radio' | 'checkbox'; getActionViewItem?(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined; diff --git a/src/vs/base/browser/dompurify/cgmanifest.json b/src/vs/base/browser/dompurify/cgmanifest.json index eac506db4b6..67f049b86c8 100644 --- a/src/vs/base/browser/dompurify/cgmanifest.json +++ b/src/vs/base/browser/dompurify/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "dompurify", "repositoryUrl": "https://github.com/cure53/DOMPurify", - "commitHash": "6cfcdf56269b892550af80baa7c1fa5b680e5db7" + "commitHash": "c96c9df61f1070146c0c13078e85b33d8fed3e51" } }, "license": "Apache 2.0", - "version": "2.3.1" + "version": "3.0.5" } ], "version": 1 diff --git a/src/vs/base/browser/dompurify/dompurify.d.ts b/src/vs/base/browser/dompurify/dompurify.d.ts index 14b0330711c..737515a3cbc 100644 --- a/src/vs/base/browser/dompurify/dompurify.d.ts +++ b/src/vs/base/browser/dompurify/dompurify.d.ts @@ -1,4 +1,4 @@ -// Type definitions for DOM Purify 2.2 +// Type definitions for DOM Purify 3.0 // Project: https://github.com/cure53/DOMPurify // Definitions by: Dave Taylor https://github.com/davetayls // Samira Bazuzi @@ -7,27 +7,49 @@ // Piotr Błażejewicz // Nicholas Ellul // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Minimum TypeScript Version: 4.5 export as namespace DOMPurify; export = DOMPurify; declare const DOMPurify: createDOMPurifyI; +type WindowLike = Pick< + typeof globalThis, + | 'NodeFilter' + | 'Node' + | 'Element' + | 'HTMLTemplateElement' + | 'DocumentFragment' + | 'HTMLFormElement' + | 'DOMParser' + | 'NamedNodeMap' +>; + interface createDOMPurifyI extends DOMPurify.DOMPurifyI { - (window?: Window): DOMPurify.DOMPurifyI; + (window?: Window | WindowLike): DOMPurify.DOMPurifyI; } declare namespace DOMPurify { interface DOMPurifyI { sanitize(source: string | Node): string; sanitize(source: string | Node, config: Config & { RETURN_TRUSTED_TYPE: true }): TrustedHTML; - sanitize(source: string | Node, config: Config & { RETURN_DOM_FRAGMENT?: false | undefined; RETURN_DOM?: false | undefined }): string; + sanitize( + source: string | Node, + config: Config & { RETURN_DOM_FRAGMENT?: false | undefined; RETURN_DOM?: false | undefined }, + ): string; sanitize(source: string | Node, config: Config & { RETURN_DOM_FRAGMENT: true }): DocumentFragment; sanitize(source: string | Node, config: Config & { RETURN_DOM: true }): HTMLElement; sanitize(source: string | Node, config: Config): string | HTMLElement | DocumentFragment; - addHook(hook: 'uponSanitizeElement', cb: (currentNode: Element, data: SanitizeElementHookEvent, config: Config) => void): void; - addHook(hook: 'uponSanitizeAttribute', cb: (currentNode: Element, data: SanitizeAttributeHookEvent, config: Config) => void): void; + addHook( + hook: 'uponSanitizeElement', + cb: (currentNode: Element, data: SanitizeElementHookEvent, config: Config) => void, + ): void; + addHook( + hook: 'uponSanitizeAttribute', + cb: (currentNode: Element, data: SanitizeAttributeHookEvent, config: Config) => void, + ): void; addHook(hook: HookName, cb: (currentNode: Element, data: HookEvent, config: Config) => void): void; setConfig(cfg: Config): void; @@ -47,18 +69,26 @@ declare namespace DOMPurify { ADD_ATTR?: string[] | undefined; ADD_DATA_URI_TAGS?: string[] | undefined; ADD_TAGS?: string[] | undefined; + ADD_URI_SAFE_ATTR?: string[] | undefined; + ALLOW_ARIA_ATTR?: boolean | undefined; ALLOW_DATA_ATTR?: boolean | undefined; + ALLOW_UNKNOWN_PROTOCOLS?: boolean | undefined; + ALLOW_SELF_CLOSE_IN_ATTR?: boolean | undefined; ALLOWED_ATTR?: string[] | undefined; ALLOWED_TAGS?: string[] | undefined; + ALLOWED_NAMESPACES?: string[] | undefined; + ALLOWED_URI_REGEXP?: RegExp | undefined; FORBID_ATTR?: string[] | undefined; + FORBID_CONTENTS?: string[] | undefined; FORBID_TAGS?: string[] | undefined; FORCE_BODY?: boolean | undefined; + IN_PLACE?: boolean | undefined; KEEP_CONTENT?: boolean | undefined; /** * change the default namespace from HTML to something different */ NAMESPACE?: string | undefined; - RETURN_DOM?: boolean | undefined; + PARSER_MEDIA_TYPE?: string | undefined; RETURN_DOM_FRAGMENT?: boolean | undefined; /** * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false` @@ -66,14 +96,27 @@ declare namespace DOMPurify { * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/ */ RETURN_DOM_IMPORT?: boolean | undefined; + RETURN_DOM?: boolean | undefined; RETURN_TRUSTED_TYPE?: boolean | undefined; - SANITIZE_DOM?: boolean | undefined; - WHOLE_DOCUMENT?: boolean | undefined; - ALLOWED_URI_REGEXP?: RegExp | undefined; SAFE_FOR_TEMPLATES?: boolean | undefined; - ALLOW_UNKNOWN_PROTOCOLS?: boolean | undefined; - USE_PROFILES?: false | { mathMl?: boolean | undefined; svg?: boolean | undefined; svgFilters?: boolean | undefined; html?: boolean | undefined } | undefined; - IN_PLACE?: boolean | undefined; + SANITIZE_DOM?: boolean | undefined; + /** @default false */ + SANITIZE_NAMED_PROPS?: boolean | undefined; + USE_PROFILES?: + | false + | { + mathMl?: boolean | undefined; + svg?: boolean | undefined; + svgFilters?: boolean | undefined; + html?: boolean | undefined; + } + | undefined; + WHOLE_DOCUMENT?: boolean | undefined; + CUSTOM_ELEMENT_HANDLING?: { + tagNameCheck?: RegExp | ((tagName: string) => boolean) | null | undefined; + attributeNameCheck?: RegExp | ((lcName: string) => boolean) | null | undefined; + allowCustomizedBuiltInElements?: boolean | undefined; + }; } type HookName = diff --git a/src/vs/base/browser/dompurify/dompurify.js b/src/vs/base/browser/dompurify/dompurify.js index 7563b15ba44..d43b10a85d1 100644 --- a/src/vs/base/browser/dompurify/dompurify.js +++ b/src/vs/base/browser/dompurify/dompurify.js @@ -1,1366 +1,1622 @@ -/*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */ +/*! @license DOMPurify 3.0.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.5/LICENSE */ -function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } +const { + entries, + setPrototypeOf, + isFrozen, + getPrototypeOf, + getOwnPropertyDescriptor +} = Object; +let { + freeze, + seal, + create +} = Object; // eslint-disable-line import/no-mutable-exports -var hasOwnProperty = Object.hasOwnProperty, - setPrototypeOf = Object.setPrototypeOf, - isFrozen = Object.isFrozen, - getPrototypeOf = Object.getPrototypeOf, - getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; -var freeze = Object.freeze, - seal = Object.seal, - create = Object.create; // eslint-disable-line import/no-mutable-exports - -var _ref = typeof Reflect !== 'undefined' && Reflect, - apply = _ref.apply, - construct = _ref.construct; +let { + apply, + construct +} = typeof Reflect !== 'undefined' && Reflect; if (!apply) { - apply = function apply(fun, thisValue, args) { - return fun.apply(thisValue, args); - }; + apply = function apply(fun, thisValue, args) { + return fun.apply(thisValue, args); + }; } if (!freeze) { - freeze = function freeze(x) { - return x; - }; + freeze = function freeze(x) { + return x; + }; } if (!seal) { - seal = function seal(x) { - return x; - }; + seal = function seal(x) { + return x; + }; } if (!construct) { - construct = function construct(Func, args) { - return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))(); - }; + construct = function construct(Func, args) { + return new Func(...args); + }; } -var arrayForEach = unapply(Array.prototype.forEach); -var arrayPop = unapply(Array.prototype.pop); -var arrayPush = unapply(Array.prototype.push); - -var stringToLowerCase = unapply(String.prototype.toLowerCase); -var stringMatch = unapply(String.prototype.match); -var stringReplace = unapply(String.prototype.replace); -var stringIndexOf = unapply(String.prototype.indexOf); -var stringTrim = unapply(String.prototype.trim); - -var regExpTest = unapply(RegExp.prototype.test); - -var typeErrorCreate = unconstruct(TypeError); - +const arrayForEach = unapply(Array.prototype.forEach); +const arrayPop = unapply(Array.prototype.pop); +const arrayPush = unapply(Array.prototype.push); +const stringToLowerCase = unapply(String.prototype.toLowerCase); +const stringToString = unapply(String.prototype.toString); +const stringMatch = unapply(String.prototype.match); +const stringReplace = unapply(String.prototype.replace); +const stringIndexOf = unapply(String.prototype.indexOf); +const stringTrim = unapply(String.prototype.trim); +const regExpTest = unapply(RegExp.prototype.test); +const typeErrorCreate = unconstruct(TypeError); function unapply(func) { - return function (thisArg) { - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } + return function (thisArg) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } - return apply(func, thisArg, args); - }; + return apply(func, thisArg, args); + }; } - function unconstruct(func) { - return function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } + return function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } - return construct(func, args); - }; + return construct(func, args); + }; } - /* Add properties to a lookup table */ -function addToSet(set, array) { - if (setPrototypeOf) { - // Make 'in' and truthy checks like Boolean(set.constructor) - // independent of any properties defined on Object.prototype. - // Prevent prototype setters from intercepting set as a this value. - setPrototypeOf(set, null); - } - var l = array.length; - while (l--) { - var element = array[l]; - if (typeof element === 'string') { - var lcElement = stringToLowerCase(element); - if (lcElement !== element) { - // Config presets (e.g. tags.js, attrs.js) are immutable. - if (!isFrozen(array)) { - array[l] = lcElement; - } +function addToSet(set, array, transformCaseFunc) { + var _transformCaseFunc; - element = lcElement; - } - } + transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase; - set[element] = true; - } + if (setPrototypeOf) { + // Make 'in' and truthy checks like Boolean(set.constructor) + // independent of any properties defined on Object.prototype. + // Prevent prototype setters from intercepting set as a this value. + setPrototypeOf(set, null); + } - return set; + let l = array.length; + + while (l--) { + let element = array[l]; + + if (typeof element === 'string') { + const lcElement = transformCaseFunc(element); + + if (lcElement !== element) { + // Config presets (e.g. tags.js, attrs.js) are immutable. + if (!isFrozen(array)) { + array[l] = lcElement; + } + + element = lcElement; + } + } + + set[element] = true; + } + + return set; } - /* Shallow clone an object */ + function clone(object) { - var newObject = create(null); + const newObject = create(null); - var property = void 0; - for (property in object) { - if (apply(hasOwnProperty, object, [property])) { - newObject[property] = object[property]; - } - } + for (const [property, value] of entries(object)) { + newObject[property] = value; + } - return newObject; + return newObject; } +/* This method automatically checks if the prop is function + * or getter and behaves accordingly. */ -/* IE10 doesn't support __lookupGetter__ so lets' - * simulate it. It also automatically checks - * if the prop is function or getter and behaves - * accordingly. */ function lookupGetter(object, prop) { - while (object !== null) { - var desc = getOwnPropertyDescriptor(object, prop); - if (desc) { - if (desc.get) { - return unapply(desc.get); - } + while (object !== null) { + const desc = getOwnPropertyDescriptor(object, prop); - if (typeof desc.value === 'function') { - return unapply(desc.value); - } - } + if (desc) { + if (desc.get) { + return unapply(desc.get); + } - object = getPrototypeOf(object); - } + if (typeof desc.value === 'function') { + return unapply(desc.value); + } + } - function fallbackValue(element) { - console.warn('fallback value for', element); - return null; - } + object = getPrototypeOf(object); + } - return fallbackValue; + function fallbackValue(element) { + console.warn('fallback value for', element); + return null; + } + + return fallbackValue; } -var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); +const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG -// SVG -var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']); - -var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); - -// List of SVG elements that are disallowed by default. +const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']); +const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default. // We still need to know them so that we can do namespace // checks properly in case one wants to add them to // allow-list. -var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']); -var mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']); - -// Similarly to SVG, we want to know all MathML elements, +const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']); +const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']); // Similarly to SVG, we want to know all MathML elements, // even those that we disallow by default. -var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']); -var text = freeze(['#text']); +const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']); +const text = freeze(['#text']); -var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']); +const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']); +const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']); +const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']); +const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); -var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']); +const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode -var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']); +const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm); +const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm); +const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape -var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); +const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape -// eslint-disable-next-line unicorn/better-regex -var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode -var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm); -var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape -var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape -var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape +const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape ); -var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); -var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex +const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); +const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); +const DOCTYPE_NAME = seal(/^html$/i); -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - -function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } - -var getGlobal = function getGlobal() { - return typeof window === 'undefined' ? null : window; -}; +var EXPRESSIONS = /*#__PURE__*/Object.freeze({ + __proto__: null, + MUSTACHE_EXPR: MUSTACHE_EXPR, + ERB_EXPR: ERB_EXPR, + TMPLIT_EXPR: TMPLIT_EXPR, + DATA_ATTR: DATA_ATTR, + ARIA_ATTR: ARIA_ATTR, + IS_ALLOWED_URI: IS_ALLOWED_URI, + IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA, + ATTR_WHITESPACE: ATTR_WHITESPACE, + DOCTYPE_NAME: DOCTYPE_NAME +}); +const getGlobal = () => typeof window === 'undefined' ? null : window; /** * Creates a no-op policy for internal use only. * Don't export this function outside this module! * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory. - * @param {Document} document The document object (to determine policy name suffix) + * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix). * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types - * are not supported). + * are not supported or creating the policy failed). */ -var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { - if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { - return null; - } - // Allow the callers to control the unique policy name - // by adding a data-tt-policy-suffix to the script element with the DOMPurify. - // Policy creation with duplicate names throws in Trusted Types. - var suffix = null; - var ATTR_NAME = 'data-tt-policy-suffix'; - if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { - suffix = document.currentScript.getAttribute(ATTR_NAME); - } - var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); +const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) { + if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') { + return null; + } // Allow the callers to control the unique policy name + // by adding a data-tt-policy-suffix to the script element with the DOMPurify. + // Policy creation with duplicate names throws in Trusted Types. - try { - return trustedTypes.createPolicy(policyName, { - createHTML: function createHTML(html$$1) { - return html$$1; - } - }); - } catch (_) { - // Policy creation failed (most likely another DOMPurify script has - // already run). Skip creating the policy, as this will only cause errors - // if TT are enforced. - console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); - return null; - } + + let suffix = null; + const ATTR_NAME = 'data-tt-policy-suffix'; + + if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) { + suffix = purifyHostElement.getAttribute(ATTR_NAME); + } + + const policyName = 'dompurify' + (suffix ? '#' + suffix : ''); + + try { + return trustedTypes.createPolicy(policyName, { + createHTML(html) { + return html; + }, + + createScriptURL(scriptUrl) { + return scriptUrl; + } + + }); + } catch (_) { + // Policy creation failed (most likely another DOMPurify script has + // already run). Skip creating the policy, as this will only cause errors + // if TT are enforced. + console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); + return null; + } }; function createDOMPurify() { - var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); - - var DOMPurify = function DOMPurify(root) { - return createDOMPurify(root); - }; - - /** - * Version label, exposed for easier checks - * if DOMPurify is up to date or not - */ - DOMPurify.version = '2.3.1'; - - /** - * Array of elements that DOMPurify removed during sanitation. - * Empty if nothing was removed. - */ - DOMPurify.removed = []; - - if (!window || !window.document || window.document.nodeType !== 9) { - // Not running in a browser, provide a factory function - // so that you can pass your own Window - DOMPurify.isSupported = false; - - return DOMPurify; - } - - var originalDocument = window.document; - - var document = window.document; - var DocumentFragment = window.DocumentFragment, - HTMLTemplateElement = window.HTMLTemplateElement, - Node = window.Node, - Element = window.Element, - NodeFilter = window.NodeFilter, - _window$NamedNodeMap = window.NamedNodeMap, - NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, - Text = window.Text, - Comment = window.Comment, - DOMParser = window.DOMParser, - trustedTypes = window.trustedTypes; - - - var ElementPrototype = Element.prototype; - - var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); - var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling'); - var getChildNodes = lookupGetter(ElementPrototype, 'childNodes'); - var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); - - // As per issue #47, the web-components registry is inherited by a - // new document created via createHTMLDocument. As per the spec - // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) - // a new empty registry is used when creating a template contents owner - // document, so we use that as our parent document to ensure nothing - // is inherited. - if (typeof HTMLTemplateElement === 'function') { - var template = document.createElement('template'); - if (template.content && template.content.ownerDocument) { - document = template.content.ownerDocument; - } - } - - var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); - var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : ''; - - var _document = document, - implementation = _document.implementation, - createNodeIterator = _document.createNodeIterator, - createDocumentFragment = _document.createDocumentFragment, - getElementsByTagName = _document.getElementsByTagName; - var importNode = originalDocument.importNode; - - - var documentMode = {}; - try { - documentMode = clone(document).documentMode ? document.documentMode : {}; - } catch (_) {} - - var hooks = {}; - - /** - * Expose whether this browser supports running the full DOMPurify. - */ - DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; - - var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR, - ERB_EXPR$$1 = ERB_EXPR, - DATA_ATTR$$1 = DATA_ATTR, - ARIA_ATTR$$1 = ARIA_ATTR, - IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA, - ATTR_WHITESPACE$$1 = ATTR_WHITESPACE; - var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI; - - /** - * We consider the elements and attributes below to be safe. Ideally - * don't add any new ones but feel free to remove unwanted ones. - */ - - /* allowed element names */ - - var ALLOWED_TAGS = null; - var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text))); - - /* Allowed attribute names */ - var ALLOWED_ATTR = null; - var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml))); - - /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ - var FORBID_TAGS = null; - - /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ - var FORBID_ATTR = null; - - /* Decide if ARIA attributes are okay */ - var ALLOW_ARIA_ATTR = true; - - /* Decide if custom data attributes are okay */ - var ALLOW_DATA_ATTR = true; - - /* Decide if unknown protocols are okay */ - var ALLOW_UNKNOWN_PROTOCOLS = false; - - /* Output should be safe for common template engines. - * This means, DOMPurify removes data attributes, mustaches and ERB - */ - var SAFE_FOR_TEMPLATES = false; - - /* Decide if document with ... should be returned */ - var WHOLE_DOCUMENT = false; - - /* Track whether config is already set on this instance of DOMPurify. */ - var SET_CONFIG = false; - - /* Decide if all elements (e.g. style, script) must be children of - * document.body. By default, browsers might move them to document.head */ - var FORCE_BODY = false; - - /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html - * string (or a TrustedHTML object if Trusted Types are supported). - * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead - */ - var RETURN_DOM = false; - - /* Decide if a DOM `DocumentFragment` should be returned, instead of a html - * string (or a TrustedHTML object if Trusted Types are supported) */ - var RETURN_DOM_FRAGMENT = false; - - /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM - * `Node` is imported into the current `Document`. If this flag is not enabled the - * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by - * DOMPurify. - * - * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false` - * might cause XSS from attacks hidden in closed shadowroots in case the browser - * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/ - */ - var RETURN_DOM_IMPORT = true; - - /* Try to return a Trusted Type object instead of a string, return a string in - * case Trusted Types are not supported */ - var RETURN_TRUSTED_TYPE = false; - - /* Output should be free from DOM clobbering attacks? */ - var SANITIZE_DOM = true; - - /* Keep element content when removing element? */ - var KEEP_CONTENT = true; - - /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead - * of importing it into a new Document and returning a sanitized copy */ - var IN_PLACE = false; - - /* Allow usage of profiles like html, svg and mathMl */ - var USE_PROFILES = {}; - - /* Tags to ignore content of when KEEP_CONTENT is true */ - var FORBID_CONTENTS = null; - var DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']); - - /* Tags that are safe for data: URIs */ - var DATA_URI_TAGS = null; - var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']); - - /* Attributes safe for values like "javascript:" */ - var URI_SAFE_ATTRIBUTES = null; - var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']); - - var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; - var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; - var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; - /* Document namespace */ - var NAMESPACE = HTML_NAMESPACE; - var IS_EMPTY_INPUT = false; - - /* Keep a reference to config to pass to hooks */ - var CONFIG = null; - - /* Ideally, do not touch anything below this line */ - /* ______________________________________________ */ - - var formElement = document.createElement('form'); - - /** - * _parseConfig - * - * @param {Object} cfg optional config literal - */ - // eslint-disable-next-line complexity - var _parseConfig = function _parseConfig(cfg) { - if (CONFIG && CONFIG === cfg) { - return; - } - - /* Shield configuration object from tampering */ - if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') { - cfg = {}; - } - - /* Shield configuration object from prototype pollution */ - cfg = clone(cfg); - - /* Set configuration parameters */ - ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; - ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; - URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; - DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; - FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS; - FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; - FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; - USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; - ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true - ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true - ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false - SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false - WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false - RETURN_DOM = cfg.RETURN_DOM || false; // Default false - RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false - RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true - RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false - FORCE_BODY = cfg.FORCE_BODY || false; // Default false - SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true - KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true - IN_PLACE = cfg.IN_PLACE || false; // Default false - IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1; - NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE; - if (SAFE_FOR_TEMPLATES) { - ALLOW_DATA_ATTR = false; - } - - if (RETURN_DOM_FRAGMENT) { - RETURN_DOM = true; - } - - /* Parse profile info */ - if (USE_PROFILES) { - ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text))); - ALLOWED_ATTR = []; - if (USE_PROFILES.html === true) { - addToSet(ALLOWED_TAGS, html); - addToSet(ALLOWED_ATTR, html$1); - } - - if (USE_PROFILES.svg === true) { - addToSet(ALLOWED_TAGS, svg); - addToSet(ALLOWED_ATTR, svg$1); - addToSet(ALLOWED_ATTR, xml); - } - - if (USE_PROFILES.svgFilters === true) { - addToSet(ALLOWED_TAGS, svgFilters); - addToSet(ALLOWED_ATTR, svg$1); - addToSet(ALLOWED_ATTR, xml); - } - - if (USE_PROFILES.mathMl === true) { - addToSet(ALLOWED_TAGS, mathMl); - addToSet(ALLOWED_ATTR, mathMl$1); - addToSet(ALLOWED_ATTR, xml); - } - } - - /* Merge configuration parameters */ - if (cfg.ADD_TAGS) { - if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { - ALLOWED_TAGS = clone(ALLOWED_TAGS); - } - - addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); - } - - if (cfg.ADD_ATTR) { - if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { - ALLOWED_ATTR = clone(ALLOWED_ATTR); - } - - addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); - } - - if (cfg.ADD_URI_SAFE_ATTR) { - addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); - } - - if (cfg.FORBID_CONTENTS) { - if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) { - FORBID_CONTENTS = clone(FORBID_CONTENTS); - } - - addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS); - } - - /* Add #text in case KEEP_CONTENT is set to true */ - if (KEEP_CONTENT) { - ALLOWED_TAGS['#text'] = true; - } - - /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ - if (WHOLE_DOCUMENT) { - addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); - } - - /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ - if (ALLOWED_TAGS.table) { - addToSet(ALLOWED_TAGS, ['tbody']); - delete FORBID_TAGS.tbody; - } - - // Prevent further manipulation of configuration. - // Not available in IE8, Safari 5, etc. - if (freeze) { - freeze(cfg); - } - - CONFIG = cfg; - }; - - var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); - - var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); - - /* Keep track of all possible SVG and MathML tags - * so that we can perform the namespace checks - * correctly. */ - var ALL_SVG_TAGS = addToSet({}, svg); - addToSet(ALL_SVG_TAGS, svgFilters); - addToSet(ALL_SVG_TAGS, svgDisallowed); - - var ALL_MATHML_TAGS = addToSet({}, mathMl); - addToSet(ALL_MATHML_TAGS, mathMlDisallowed); - - /** - * - * - * @param {Element} element a DOM element whose namespace is being checked - * @returns {boolean} Return false if the element has a - * namespace that a spec-compliant parser would never - * return. Return true otherwise. - */ - var _checkValidNamespace = function _checkValidNamespace(element) { - var parent = getParentNode(element); - - // In JSDOM, if we're inside shadow DOM, then parentNode - // can be null. We just simulate parent in this case. - if (!parent || !parent.tagName) { - parent = { - namespaceURI: HTML_NAMESPACE, - tagName: 'template' - }; - } - - var tagName = stringToLowerCase(element.tagName); - var parentTagName = stringToLowerCase(parent.tagName); - - if (element.namespaceURI === SVG_NAMESPACE) { - // The only way to switch from HTML namespace to SVG - // is via . If it happens via any other tag, then - // it should be killed. - if (parent.namespaceURI === HTML_NAMESPACE) { - return tagName === 'svg'; - } - - // The only way to switch from MathML to SVG is via - // svg if parent is either or MathML - // text integration points. - if (parent.namespaceURI === MATHML_NAMESPACE) { - return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); - } - - // We only allow elements that are defined in SVG - // spec. All others are disallowed in SVG namespace. - return Boolean(ALL_SVG_TAGS[tagName]); - } - - if (element.namespaceURI === MATHML_NAMESPACE) { - // The only way to switch from HTML namespace to MathML - // is via . If it happens via any other tag, then - // it should be killed. - if (parent.namespaceURI === HTML_NAMESPACE) { - return tagName === 'math'; - } - - // The only way to switch from SVG to MathML is via - // and HTML integration points - if (parent.namespaceURI === SVG_NAMESPACE) { - return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; - } - - // We only allow elements that are defined in MathML - // spec. All others are disallowed in MathML namespace. - return Boolean(ALL_MATHML_TAGS[tagName]); - } - - if (element.namespaceURI === HTML_NAMESPACE) { - // The only way to switch from SVG to HTML is via - // HTML integration points, and from MathML to HTML - // is via MathML text integration points - if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { - return false; - } - - if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { - return false; - } - - // Certain elements are allowed in both SVG and HTML - // namespace. We need to specify them explicitly - // so that they don't get erronously deleted from - // HTML namespace. - var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']); - - // We disallow tags that are specific for MathML - // or SVG and should never appear in HTML namespace - return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]); - } - - // The code should never reach this place (this means - // that the element somehow got namespace that is not - // HTML, SVG or MathML). Return false just in case. - return false; - }; - - /** - * _forceRemove - * - * @param {Node} node a DOM node - */ - var _forceRemove = function _forceRemove(node) { - arrayPush(DOMPurify.removed, { element: node }); - try { - // eslint-disable-next-line unicorn/prefer-dom-node-remove - node.parentNode.removeChild(node); - } catch (_) { - try { - node.outerHTML = emptyHTML; - } catch (_) { - node.remove(); - } - } - }; - - /** - * _removeAttribute - * - * @param {String} name an Attribute name - * @param {Node} node a DOM node - */ - var _removeAttribute = function _removeAttribute(name, node) { - try { - arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node - }); - } catch (_) { - arrayPush(DOMPurify.removed, { - attribute: null, - from: node - }); - } - - node.removeAttribute(name); - - // We void attribute values for unremovable "is"" attributes - if (name === 'is' && !ALLOWED_ATTR[name]) { - if (RETURN_DOM || RETURN_DOM_FRAGMENT) { - try { - _forceRemove(node); - } catch (_) {} - } else { - try { - node.setAttribute(name, ''); - } catch (_) {} - } - } - }; - - /** - * _initDocument - * - * @param {String} dirty a string of dirty markup - * @return {Document} a DOM, filled with the dirty markup - */ - var _initDocument = function _initDocument(dirty) { - /* Create a HTML document */ - var doc = void 0; - var leadingWhitespace = void 0; - - if (FORCE_BODY) { - dirty = '' + dirty; - } else { - /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ - var matches = stringMatch(dirty, /^[\r\n\t ]+/); - leadingWhitespace = matches && matches[0]; - } - - var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; - /* - * Use the DOMParser API by default, fallback later if needs be - * DOMParser not work for svg when has multiple root element. - */ - if (NAMESPACE === HTML_NAMESPACE) { - try { - doc = new DOMParser().parseFromString(dirtyPayload, 'text/html'); - } catch (_) {} - } - - /* Use createHTMLDocument in case DOMParser is not available */ - if (!doc || !doc.documentElement) { - doc = implementation.createDocument(NAMESPACE, 'template', null); - try { - doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload; - } catch (_) { - // Syntax error if dirtyPayload is invalid xml - } - } - - var body = doc.body || doc.documentElement; - - if (dirty && leadingWhitespace) { - body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null); - } - - /* Work on whole document or just its body */ - if (NAMESPACE === HTML_NAMESPACE) { - return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; - } - - return WHOLE_DOCUMENT ? doc.documentElement : body; - }; - - /** - * _createIterator - * - * @param {Document} root document/fragment to create iterator for - * @return {Iterator} iterator instance - */ - var _createIterator = function _createIterator(root) { - return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false); - }; - - /** - * _isClobbered - * - * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe - */ - var _isClobbered = function _isClobbered(elm) { - if (elm instanceof Text || elm instanceof Comment) { - return false; - } - - if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') { - return true; - } - - return false; - }; - - /** - * _isNode - * - * @param {Node} obj object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node - */ - var _isNode = function _isNode(object) { - return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; - }; - - /** - * _executeHook - * Execute user configurable hooks - * - * @param {String} entryPoint Name of the hook's entry point - * @param {Node} currentNode node to work on with the hook - * @param {Object} data additional hook parameters - */ - var _executeHook = function _executeHook(entryPoint, currentNode, data) { - if (!hooks[entryPoint]) { - return; - } - - arrayForEach(hooks[entryPoint], function (hook) { - hook.call(DOMPurify, currentNode, data, CONFIG); - }); - }; - - /** - * _sanitizeElements - * - * @protect nodeName - * @protect textContent - * @protect removeChild - * - * @param {Node} currentNode to check for permission to exist - * @return {Boolean} true if node was killed, false if left alive - */ - var _sanitizeElements = function _sanitizeElements(currentNode) { - var content = void 0; - - /* Execute a hook if present */ - _executeHook('beforeSanitizeElements', currentNode, null); - - /* Check if element is clobbered or can clobber */ - if (_isClobbered(currentNode)) { - _forceRemove(currentNode); - return true; - } - - /* Check if tagname contains Unicode */ - if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) { - _forceRemove(currentNode); - return true; - } - - /* Now let's check the element's type and name */ - var tagName = stringToLowerCase(currentNode.nodeName); - - /* Execute a hook if present */ - _executeHook('uponSanitizeElement', currentNode, { - tagName: tagName, - allowedTags: ALLOWED_TAGS - }); - - /* Detect mXSS attempts abusing namespace confusion */ - if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { - _forceRemove(currentNode); - return true; - } - - /* Mitigate a problem with templates inside select */ - if (tagName === 'select' && regExpTest(/' + dirty + ''; + } + + const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; + /* + * Use the DOMParser API by default, fallback later if needs be + * DOMParser not work for svg when has multiple root element. + */ + + if (NAMESPACE === HTML_NAMESPACE) { + try { + doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE); + } catch (_) { } + } + /* Use createHTMLDocument in case DOMParser is not available */ + + + if (!doc || !doc.documentElement) { + doc = implementation.createDocument(NAMESPACE, 'template', null); + + try { + doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload; + } catch (_) {// Syntax error if dirtyPayload is invalid xml + } + } + + const body = doc.body || doc.documentElement; + + if (dirty && leadingWhitespace) { + body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null); + } + /* Work on whole document or just its body */ + + + if (NAMESPACE === HTML_NAMESPACE) { + return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; + } + + return WHOLE_DOCUMENT ? doc.documentElement : body; + }; + /** + * _createIterator + * + * @param {Document} root document/fragment to create iterator for + * @return {Iterator} iterator instance + */ + + + const _createIterator = function _createIterator(root) { + return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise + NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false); + }; + /** + * _isClobbered + * + * @param {Node} elm element to check for clobbering attacks + * @return {Boolean} true if clobbered, false if safe + */ + + + const _isClobbered = function _isClobbered(elm) { + return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + }; + /** + * _isNode + * + * @param {Node} obj object to check whether it's a DOM node + * @return {Boolean} true is object is a DOM node + */ + + + const _isNode = function _isNode(object) { + return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; + }; + /** + * _executeHook + * Execute user configurable hooks + * + * @param {String} entryPoint Name of the hook's entry point + * @param {Node} currentNode node to work on with the hook + * @param {Object} data additional hook parameters + */ + + + const _executeHook = function _executeHook(entryPoint, currentNode, data) { + if (!hooks[entryPoint]) { + return; + } + + arrayForEach(hooks[entryPoint], hook => { + hook.call(DOMPurify, currentNode, data, CONFIG); + }); + }; + /** + * _sanitizeElements + * + * @protect nodeName + * @protect textContent + * @protect removeChild + * + * @param {Node} currentNode to check for permission to exist + * @return {Boolean} true if node was killed, false if left alive + */ + + + const _sanitizeElements = function _sanitizeElements(currentNode) { + let content; + /* Execute a hook if present */ + + _executeHook('beforeSanitizeElements', currentNode, null); + /* Check if element is clobbered or can clobber */ + + + if (_isClobbered(currentNode)) { + _forceRemove(currentNode); + + return true; + } + /* Now let's check the element's type and name */ + + + const tagName = transformCaseFunc(currentNode.nodeName); + /* Execute a hook if present */ + + _executeHook('uponSanitizeElement', currentNode, { + tagName, + allowedTags: ALLOWED_TAGS + }); + /* Detect mXSS attempts abusing namespace confusion */ + + + if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { + _forceRemove(currentNode); + + return true; + } + /* Remove element if anything forbids its presence */ + + + if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { + /* Check if we have a custom element to handle */ + if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) { + if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false; + if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false; + } + /* Keep content except for bad-listed elements */ + + + if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) { + const parentNode = getParentNode(currentNode) || currentNode.parentNode; + const childNodes = getChildNodes(currentNode) || currentNode.childNodes; + + if (childNodes && parentNode) { + const childCount = childNodes.length; + + for (let i = childCount - 1; i >= 0; --i) { + parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + } + } + } + + _forceRemove(currentNode); + + return true; + } + /* Check whether element has a valid namespace */ + + + if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) { + _forceRemove(currentNode); + + return true; + } + /* Make sure that older browsers don't get fallback-tag mXSS */ + + + if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) { + _forceRemove(currentNode); + + return true; + } + /* Sanitize element content to be template-safe */ + + + if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { + /* Get the element's text content */ + content = currentNode.textContent; + content = stringReplace(content, MUSTACHE_EXPR, ' '); + content = stringReplace(content, ERB_EXPR, ' '); + content = stringReplace(content, TMPLIT_EXPR, ' '); + + if (currentNode.textContent !== content) { + arrayPush(DOMPurify.removed, { + element: currentNode.cloneNode() + }); + currentNode.textContent = content; + } + } + /* Execute a hook if present */ + + + _executeHook('afterSanitizeElements', currentNode, null); + + return false; + }; + /** + * _isValidAttribute + * + * @param {string} lcTag Lowercase tag name of containing element. + * @param {string} lcName Lowercase attribute name. + * @param {string} value Attribute value. + * @return {Boolean} Returns true if `value` is valid, otherwise false. + */ + // eslint-disable-next-line complexity + + + const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { + /* Make sure attribute cannot clobber */ + if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) { + return false; + } + /* Allow valid data-* attributes: At least one character after "-" + (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) + XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804) + We don't need to check the value; it's always URI safe. */ + + + if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)); else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)); else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { + if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND + // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck + // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck + _basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND + // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck + lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))); else { + return false; + } + /* Check value is safe. First, is attr inert? If so, is safe */ + + } else if (URI_SAFE_ATTRIBUTES[lcName]); else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))); else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]); else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))); else if (value) { + return false; + } else; + + return true; + }; + /** + * _basicCustomElementCheck + * checks if at least one dash is included in tagName, and it's not the first char + * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name + * @param {string} tagName name of the tag of the node to sanitize + */ + + + const _basicCustomElementTest = function _basicCustomElementTest(tagName) { + return tagName.indexOf('-') > 0; + }; + /** + * _sanitizeAttributes + * + * @protect attributes + * @protect nodeName + * @protect removeAttribute + * @protect setAttribute + * + * @param {Node} currentNode to sanitize + */ + + + const _sanitizeAttributes = function _sanitizeAttributes(currentNode) { + let attr; + let value; + let lcName; + let l; + /* Execute a hook if present */ + + _executeHook('beforeSanitizeAttributes', currentNode, null); + + const { + attributes + } = currentNode; + /* Check if we have attributes; if not we might have a text node */ + + if (!attributes) { + return; + } + + const hookEvent = { + attrName: '', + attrValue: '', + keepAttr: true, + allowedAttributes: ALLOWED_ATTR + }; + l = attributes.length; + /* Go backwards over all attributes; safely remove bad ones */ + + while (l--) { + attr = attributes[l]; + const { + name, + namespaceURI + } = attr; + value = name === 'value' ? attr.value : stringTrim(attr.value); + lcName = transformCaseFunc(name); + /* Execute a hook if present */ + + hookEvent.attrName = lcName; + hookEvent.attrValue = value; + hookEvent.keepAttr = true; + hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set + + _executeHook('uponSanitizeAttribute', currentNode, hookEvent); + + value = hookEvent.attrValue; + /* Did the hooks approve of the attribute? */ + + if (hookEvent.forceKeepAttr) { + continue; + } + /* Remove attribute */ + + + _removeAttribute(name, currentNode); + /* Did the hooks approve of the attribute? */ + + + if (!hookEvent.keepAttr) { + continue; + } + /* Work around a security issue in jQuery 3.0 */ + + + if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) { + _removeAttribute(name, currentNode); + + continue; + } + /* Sanitize attribute content to be template-safe */ + + + if (SAFE_FOR_TEMPLATES) { + value = stringReplace(value, MUSTACHE_EXPR, ' '); + value = stringReplace(value, ERB_EXPR, ' '); + value = stringReplace(value, TMPLIT_EXPR, ' '); + } + /* Is `value` valid for this attribute? */ + + + const lcTag = transformCaseFunc(currentNode.nodeName); + + if (!_isValidAttribute(lcTag, lcName, value)) { + continue; + } + /* Full DOM Clobbering protection via namespace isolation, + * Prefix id and name attributes with `user-content-` + */ + + + if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) { + // Remove the attribute with this value + _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value + + + value = SANITIZE_NAMED_PROPS_PREFIX + value; + } + /* Handle attributes that require Trusted Types */ + + + if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') { + if (namespaceURI); else { + switch (trustedTypes.getAttributeType(lcTag, lcName)) { + case 'TrustedHTML': + { + value = trustedTypesPolicy.createHTML(value); + break; + } + + case 'TrustedScriptURL': + { + value = trustedTypesPolicy.createScriptURL(value); + break; + } + } + } + } + /* Handle invalid data-* attribute set by try-catching it */ + + + try { + if (namespaceURI) { + currentNode.setAttributeNS(namespaceURI, name, value); + } else { + /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */ + currentNode.setAttribute(name, value); + } + + arrayPop(DOMPurify.removed); + } catch (_) { } + } + /* Execute a hook if present */ + + + _executeHook('afterSanitizeAttributes', currentNode, null); + }; + /** + * _sanitizeShadowDOM + * + * @param {DocumentFragment} fragment to iterate over recursively + */ + + + const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { + let shadowNode; + + const shadowIterator = _createIterator(fragment); + /* Execute a hook if present */ + + + _executeHook('beforeSanitizeShadowDOM', fragment, null); + + while (shadowNode = shadowIterator.nextNode()) { + /* Execute a hook if present */ + _executeHook('uponSanitizeShadowNode', shadowNode, null); + /* Sanitize tags and elements */ + + + if (_sanitizeElements(shadowNode)) { + continue; + } + /* Deep shadow DOM detected */ + + + if (shadowNode.content instanceof DocumentFragment) { + _sanitizeShadowDOM(shadowNode.content); + } + /* Check attributes, sanitize if necessary */ + + + _sanitizeAttributes(shadowNode); + } + /* Execute a hook if present */ + + + _executeHook('afterSanitizeShadowDOM', fragment, null); + }; + /** + * Sanitize + * Public method providing core sanitation functionality + * + * @param {String|Node} dirty string or DOM node + * @param {Object} configuration object + */ + // eslint-disable-next-line complexity + + + DOMPurify.sanitize = function (dirty) { + let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + let body; + let importedNode; + let currentNode; + let returnNode; + /* Make sure we have a string to sanitize. + DO NOT return early, as this will return the wrong type if + the user has requested a DOM object rather than a string */ + + IS_EMPTY_INPUT = !dirty; + + if (IS_EMPTY_INPUT) { + dirty = ''; + } + /* Stringify, in case dirty is an object */ + + + if (typeof dirty !== 'string' && !_isNode(dirty)) { + if (typeof dirty.toString === 'function') { + dirty = dirty.toString(); + + if (typeof dirty !== 'string') { + throw typeErrorCreate('dirty is not a string, aborting'); + } + } else { + throw typeErrorCreate('toString is not a function'); + } + } + /* Return dirty HTML if DOMPurify cannot run */ + + + if (!DOMPurify.isSupported) { + return dirty; + } + /* Assign config vars */ + + + if (!SET_CONFIG) { + _parseConfig(cfg); + } + /* Clean up removed elements */ + + + DOMPurify.removed = []; + /* Check if dirty is correctly typed for IN_PLACE */ + + if (typeof dirty === 'string') { + IN_PLACE = false; + } + + if (IN_PLACE) { + /* Do some early pre-sanitization to avoid unsafe root nodes */ + if (dirty.nodeName) { + const tagName = transformCaseFunc(dirty.nodeName); + + if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { + throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place'); + } + } + } else if (dirty instanceof Node) { + /* If dirty is a DOM element, append to an empty document to avoid + elements being stripped by the parser */ + body = _initDocument(''); + importedNode = body.ownerDocument.importNode(dirty, true); + + if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { + /* Node is already a body, use as is */ + body = importedNode; + } else if (importedNode.nodeName === 'HTML') { + body = importedNode; + } else { + // eslint-disable-next-line unicorn/prefer-dom-node-append + body.appendChild(importedNode); + } + } else { + /* Exit directly if we have nothing to do */ + if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes + dirty.indexOf('<') === -1) { + return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; + } + /* Initialize the document to work on */ + + + body = _initDocument(dirty); + /* Check we have a DOM node from the data */ + + if (!body) { + return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : ''; + } + } + /* Remove first element node (ours) if FORCE_BODY is set */ + + + if (body && FORCE_BODY) { + _forceRemove(body.firstChild); + } + /* Get node iterator */ + + + const nodeIterator = _createIterator(IN_PLACE ? dirty : body); + /* Now start iterating over the created document */ + + + while (currentNode = nodeIterator.nextNode()) { + /* Sanitize tags and elements */ + if (_sanitizeElements(currentNode)) { + continue; + } + /* Shadow DOM detected, sanitize it */ + + + if (currentNode.content instanceof DocumentFragment) { + _sanitizeShadowDOM(currentNode.content); + } + /* Check attributes, sanitize if necessary */ + + + _sanitizeAttributes(currentNode); + } + /* If we sanitized `dirty` in-place, return it. */ + + + if (IN_PLACE) { + return dirty; + } + /* Return sanitized string or DOM */ + + + if (RETURN_DOM) { + if (RETURN_DOM_FRAGMENT) { + returnNode = createDocumentFragment.call(body.ownerDocument); + + while (body.firstChild) { + // eslint-disable-next-line unicorn/prefer-dom-node-append + returnNode.appendChild(body.firstChild); + } + } else { + returnNode = body; + } + + if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) { + /* + AdoptNode() is not used because internal state is not reset + (e.g. the past names map of a HTMLFormElement), this is safe + in theory but we would rather not risk another attack vector. + The state that is cloned by importNode() is explicitly defined + by the specs. + */ + returnNode = importNode.call(originalDocument, returnNode, true); + } + + return returnNode; + } + + let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; + /* Serialize doctype if allowed */ + + if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) { + serializedHTML = '\n' + serializedHTML; + } + /* Sanitize final string template-safe */ + + + if (SAFE_FOR_TEMPLATES) { + serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' '); + serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' '); + serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' '); + } + + return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; + }; + /** + * Public method to set the configuration once + * setConfig + * + * @param {Object} cfg configuration object + */ + + + DOMPurify.setConfig = function (cfg) { + _parseConfig(cfg); + + SET_CONFIG = true; + }; + /** + * Public method to remove the configuration + * clearConfig + * + */ + + + DOMPurify.clearConfig = function () { + CONFIG = null; + SET_CONFIG = false; + }; + /** + * Public method to check if an attribute value is valid. + * Uses last set config, if any. Otherwise, uses config defaults. + * isValidAttribute + * + * @param {string} tag Tag name of containing element. + * @param {string} attr Attribute name. + * @param {string} value Attribute value. + * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false. + */ + + + DOMPurify.isValidAttribute = function (tag, attr, value) { + /* Initialize shared config vars if necessary. */ + if (!CONFIG) { + _parseConfig({}); + } + + const lcTag = transformCaseFunc(tag); + const lcName = transformCaseFunc(attr); + return _isValidAttribute(lcTag, lcName, value); + }; + /** + * AddHook + * Public method to add DOMPurify hooks + * + * @param {String} entryPoint entry point for the hook to add + * @param {Function} hookFunction function to execute + */ + + + DOMPurify.addHook = function (entryPoint, hookFunction) { + if (typeof hookFunction !== 'function') { + return; + } + + hooks[entryPoint] = hooks[entryPoint] || []; + arrayPush(hooks[entryPoint], hookFunction); + }; + /** + * RemoveHook + * Public method to remove a DOMPurify hook at a given entryPoint + * (pops it from the stack of hooks if more are present) + * + * @param {String} entryPoint entry point for the hook to remove + * @return {Function} removed(popped) hook + */ + + + DOMPurify.removeHook = function (entryPoint) { + if (hooks[entryPoint]) { + return arrayPop(hooks[entryPoint]); + } + }; + /** + * RemoveHooks + * Public method to remove all DOMPurify hooks at a given entryPoint + * + * @param {String} entryPoint entry point for the hooks to remove + */ + + + DOMPurify.removeHooks = function (entryPoint) { + if (hooks[entryPoint]) { + hooks[entryPoint] = []; + } + }; + /** + * RemoveAllHooks + * Public method to remove all DOMPurify hooks + * + */ + + + DOMPurify.removeAllHooks = function () { + hooks = {}; + }; + + return DOMPurify; } var purify = createDOMPurify(); diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts index a4da8d78445..d0b5b3aab21 100644 --- a/src/vs/base/browser/ui/aria/aria.ts +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -31,7 +31,6 @@ export function setARIAContainer(parent: HTMLElement) { const createStatusContainer = () => { const element = document.createElement('div'); element.className = 'monaco-status'; - element.setAttribute('role', 'complementary'); element.setAttribute('aria-live', 'polite'); element.setAttribute('aria-atomic', 'true'); ariaContainer.appendChild(element); diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 04931cca3f0..d2c9f9464ae 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -5,9 +5,11 @@ import { BrowserFeatures } from 'vs/base/browser/canIUse'; import * as DOM from 'vs/base/browser/dom'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { Range } from 'vs/base/common/range'; +import { OmitOptional } from 'vs/base/common/types'; import 'vs/css!./contextview'; export const enum ContextViewDOMPosition { @@ -23,6 +25,12 @@ export interface IAnchor { height?: number; } +export function isAnchor(obj: unknown): obj is IAnchor | OmitOptional { + const anchor = obj as IAnchor | OmitOptional | undefined; + + return !!anchor && typeof anchor.x === 'number' && typeof anchor.y === 'number'; +} + export const enum AnchorAlignment { LEFT, RIGHT } @@ -36,7 +44,13 @@ export const enum AnchorAxisAlignment { } export interface IDelegate { - getAnchor(): HTMLElement | IAnchor; + /** + * The anchor where to position the context view. + * Use a `HTMLElement` to position the view at the element, + * a `StandardMouseEvent` to position it at the mouse position + * or an `IAnchor` to position it at a specific location. + */ + getAnchor(): HTMLElement | StandardMouseEvent | IAnchor; render(container: HTMLElement): IDisposable | null; focus?(): void; layout?(): void; @@ -271,13 +285,24 @@ export class ContextView extends Disposable { width: elementPosition.width * zoom, height: elementPosition.height * zoom }; - } else { + } else if (isAnchor(anchor)) { around = { top: anchor.y, left: anchor.x, width: anchor.width || 1, height: anchor.height || 2 }; + } else { + around = { + top: anchor.posy, + left: anchor.posx, + // We are about to position the context view where the mouse + // cursor is. To prevent the view being exactly under the mouse + // when showing and thus potentially triggering an action within, + // we treat the mouse location like a small sized block element. + width: 2, + height: 2 + }; } const viewSizeWidth = DOM.getTotalWidth(this.view); diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index aee3cad2301..2562b2fa153 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -455,7 +455,8 @@ export class Grid extends Disposable { if (sizing?.type === 'distribute') { gridViewSizing = GridViewSizing.Distribute; } else if (sizing?.type === 'auto') { - gridViewSizing = GridViewSizing.Auto(0); + const index = location[location.length - 1]; + gridViewSizing = GridViewSizing.Auto(index === 0 ? 1 : index - 1); } this.gridview.removeView(location, gridViewSizing); diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index d24e1d26811..c89c6a7a063 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -356,6 +356,13 @@ class BranchNode implements ISplitView, IDisposable { private _boundarySashes: IRelativeBoundarySashes = {}; get boundarySashes(): IRelativeBoundarySashes { return this._boundarySashes; } set boundarySashes(boundarySashes: IRelativeBoundarySashes) { + if (this._boundarySashes.start === boundarySashes.start + && this._boundarySashes.end === boundarySashes.end + && this._boundarySashes.orthogonalStart === boundarySashes.orthogonalStart + && this._boundarySashes.orthogonalEnd === boundarySashes.orthogonalEnd) { + return; + } + this._boundarySashes = boundarySashes; this.splitview.orthogonalStartSash = boundarySashes.orthogonalStart; @@ -498,67 +505,22 @@ class BranchNode implements ISplitView, IDisposable { index = validateIndex(index, this.children.length); this.splitview.addView(node, size, index, skipLayout); - this._addChild(node, index); - this.onDidChildrenChange(); - } - - private _addChild(node: Node, index: number): void { - const first = index === 0; - const last = index === this.children.length; this.children.splice(index, 0, node); - node.boundarySashes = { - start: this.boundarySashes.orthogonalStart, - end: this.boundarySashes.orthogonalEnd, - orthogonalStart: first ? this.boundarySashes.start : this.splitview.sashes[index - 1], - orthogonalEnd: last ? this.boundarySashes.end : this.splitview.sashes[index], - }; - - if (!first) { - this.children[index - 1].boundarySashes = { - ...this.children[index - 1].boundarySashes, - orthogonalEnd: this.splitview.sashes[index - 1] - }; - } - - if (!last) { - this.children[index + 1].boundarySashes = { - ...this.children[index + 1].boundarySashes, - orthogonalStart: this.splitview.sashes[index] - }; - } + this.updateBoundarySashes(); + this.onDidChildrenChange(); } removeChild(index: number, sizing?: Sizing): void { index = validateIndex(index, this.children.length); this.splitview.removeView(index, sizing); - this._removeChild(index); + this.children.splice(index, 1); + + this.updateBoundarySashes(); this.onDidChildrenChange(); } - private _removeChild(index: number): Node { - const first = index === 0; - const last = index === this.children.length - 1; - const [child] = this.children.splice(index, 1); - - if (!first) { - this.children[index - 1].boundarySashes = { - ...this.children[index - 1].boundarySashes, - orthogonalEnd: this.splitview.sashes[index - 1] - }; - } - - if (!last) { // [0,1,2,3] (2) => [0,1,3] - this.children[index].boundarySashes = { - ...this.children[index].boundarySashes, - orthogonalStart: this.splitview.sashes[Math.max(index - 1, 0)] - }; - } - - return child; - } - moveChild(from: number, to: number): void { from = validateIndex(from, this.children.length); to = validateIndex(to, this.children.length); @@ -568,14 +530,13 @@ class BranchNode implements ISplitView, IDisposable { } if (from < to) { - to--; + to -= 1; } this.splitview.moveView(from, to); + this.children.splice(to, 0, this.children.splice(from, 1)[0]); - const child = this._removeChild(from); - this._addChild(child, to); - + this.updateBoundarySashes(); this.onDidChildrenChange(); } @@ -649,6 +610,17 @@ class BranchNode implements ISplitView, IDisposable { return this.splitview.getViewCachedVisibleSize(index); } + private updateBoundarySashes(): void { + for (let i = 0; i < this.children.length; i++) { + this.children[i].boundarySashes = { + start: this.boundarySashes.orthogonalStart, + end: this.boundarySashes.orthogonalEnd, + orthogonalStart: i === 0 ? this.boundarySashes.start : this.splitview.sashes[i - 1], + orthogonalEnd: i === this.children.length - 1 ? this.boundarySashes.end : this.splitview.sashes[i], + }; + } + } + private onDidChildrenChange(): void { this.updateChildrenEvents(); this._onDidChange.fire(undefined); diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index 0ce58199354..f008173e716 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -27,7 +27,7 @@ } .monaco-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { - max-width: 500px; + max-width: var(--hover-maxWidth, 500px); word-wrap: break-word; } diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 1dcc1751ff8..af621b9047e 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -685,6 +685,19 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge } } + public prependHistory(restoredHistory: string[]): void { + const newHistory = this.getHistory(); + this.clearHistory(); + + restoredHistory.forEach((item) => { + this.history.add(item); + }); + + newHistory.forEach(item => { + this.history.add(item); + }); + } + public getHistory(): string[] { return this.history.getHistory(); } diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index f776ba7ecb2..2a77409cd83 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -5,6 +5,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { GestureEvent } from 'vs/base/browser/touch'; export interface IListVirtualDelegate { @@ -61,7 +62,7 @@ export interface IListContextMenuEvent { readonly browserEvent: UIEvent; readonly element: T | undefined; readonly index: number | undefined; - readonly anchor: HTMLElement | { readonly x: number; readonly y: number }; + readonly anchor: HTMLElement | IMouseEvent; } export interface IIdentityProvider { diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 3701207d187..a3d2eb16cfc 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -108,7 +108,7 @@ export interface IPagedListOptions { readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; - readonly additionalScrollHeight?: number; + readonly paddingBottom?: number; } function fromPagedListOptions(modelProvider: () => IPagedModel, options: IPagedListOptions): IListOptions { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index b68a8b413da..b73e5811121 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -56,12 +56,13 @@ export interface IListViewAccessibilityProvider { } export interface IListViewOptionsUpdate { - readonly additionalScrollHeight?: number; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; readonly mouseWheelScrollSensitivity?: number; readonly fastScrollSensitivity?: number; + readonly paddingTop?: number; + readonly paddingBottom?: number; } export interface IListViewOptions extends IListViewOptionsUpdate { @@ -298,7 +299,7 @@ export class ListView implements IListView { private setRowLineHeight: boolean; private setRowHeight: boolean; private supportDynamicHeights: boolean; - private additionalScrollHeight: number; + private paddingBottom: number; private accessibilityProvider: ListViewAccessibilityProvider; private scrollWidth: number | undefined; @@ -364,7 +365,7 @@ export class ListView implements IListView { this.items = []; this.itemId = 0; - this.rangeMap = new RangeMap(); + this.rangeMap = new RangeMap(options.paddingTop ?? 0); for (const renderer of renderers) { this.renderers.set(renderer.templateId, renderer); @@ -386,7 +387,7 @@ export class ListView implements IListView { this._horizontalScrolling = options.horizontalScrolling ?? DefaultOptions.horizontalScrolling; this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); - this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; + this.paddingBottom = typeof options.paddingBottom === 'undefined' ? 0 : options.paddingBottom; this.accessibilityProvider = new ListViewAccessibilityProvider(options.accessibilityProvider); @@ -441,8 +442,8 @@ export class ListView implements IListView { } updateOptions(options: IListViewOptionsUpdate) { - if (options.additionalScrollHeight !== undefined) { - this.additionalScrollHeight = options.additionalScrollHeight; + if (options.paddingBottom !== undefined) { + this.paddingBottom = options.paddingBottom; this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); } @@ -471,6 +472,22 @@ export class ListView implements IListView { if (scrollableOptions) { this.scrollableElement.updateOptions(scrollableOptions); } + + if (options.paddingTop !== undefined && options.paddingTop !== this.rangeMap.paddingTop) { + // trigger a rerender + const lastRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + const offset = options.paddingTop - this.rangeMap.paddingTop; + this.rangeMap.paddingTop = options.paddingTop; + + this.render(lastRenderRange, Math.max(0, this.lastRenderTop + offset), this.lastRenderHeight, undefined, undefined, true); + this.setScrollTop(this.lastRenderTop); + + this.eventuallyUpdateScrollDimensions(); + + if (this.supportDynamicHeights) { + this._rerender(this.lastRenderTop, this.lastRenderHeight); + } + } } delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { @@ -602,7 +619,7 @@ export class ListView implements IListView { // TODO@joao: improve this optimization to catch even more cases if (start === 0 && deleteCount >= this.items.length) { - this.rangeMap = new RangeMap(); + this.rangeMap = new RangeMap(this.rangeMap.paddingTop); this.rangeMap.splice(0, 0, inserted); deleted = this.items; this.items = inserted; @@ -1017,7 +1034,7 @@ export class ListView implements IListView { } get scrollHeight(): number { - return this._scrollHeight + (this.horizontalScrolling ? 10 : 0) + this.additionalScrollHeight; + return this._scrollHeight + (this.horizontalScrolling ? 10 : 0) + this.paddingBottom; } // Events diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ae34c857e71..36c5a74a948 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -27,6 +27,7 @@ import { isNumber } from 'vs/base/common/types'; import 'vs/css!./list'; import { IIdentityProvider, IKeyboardNavigationDelegate, IKeyboardNavigationLabelProvider, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IListEvent, IListGestureEvent, IListMouseEvent, IListRenderer, IListTouchEvent, IListVirtualDelegate, ListError } from './list'; import { IListView, IListViewAccessibilityProvider, IListViewDragAndDrop, IListViewOptions, IListViewOptionsUpdate, ListView } from './listView'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; interface ITraitChangeEvent { indexes: number[]; @@ -991,12 +992,13 @@ export interface IListOptions extends IListOptionsUpdate { readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; - readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; readonly smoothScrolling?: boolean; readonly scrollableElementChangeOptions?: ScrollableElementChangeOptions; readonly alwaysConsumeMouseWheel?: boolean; readonly initialSize?: Dimension; + readonly paddingTop?: number; + readonly paddingBottom?: number; } export interface IListStyles { @@ -1353,7 +1355,7 @@ export class List implements ISpliceable, IDisposable { const fromMouse = this.disposables.add(Event.chain(this.view.onContextMenu)) .filter(_ => !didJustPressContextMenuKey) - .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.pageX + 1, y: browserEvent.pageY }, browserEvent })) + .map(({ element, index, browserEvent }) => ({ element, index, anchor: new StandardMouseEvent(browserEvent), browserEvent })) .event; return Event.any>(fromKeyDown, fromKeyUp, fromMouse); diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 5656a3b32de..91f435cd822 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -91,6 +91,21 @@ export class RangeMap { private groups: IRangedGroup[] = []; private _size = 0; + private _paddingTop = 0; + + get paddingTop() { + return this._paddingTop; + } + + set paddingTop(paddingTop: number) { + this._size = this._size + paddingTop - this._paddingTop; + this._paddingTop = paddingTop; + } + + constructor(topPadding?: number) { + this._paddingTop = topPadding ?? 0; + this._size = this._paddingTop; + } splice(index: number, deleteCount: number, items: IItem[] = []): void { const diff = items.length - deleteCount; @@ -104,7 +119,7 @@ export class RangeMap { })); this.groups = concat(before, middle, after); - this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + this._size = this._paddingTop + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); } /** @@ -135,8 +150,12 @@ export class RangeMap { return -1; } + if (position < this._paddingTop) { + return 0; + } + let index = 0; - let size = 0; + let size = this._paddingTop; for (const group of this.groups) { const count = group.range.end - group.range.start; @@ -177,7 +196,7 @@ export class RangeMap { const newCount = count + groupCount; if (index < newCount) { - return position + ((index - count) * group.size); + return this._paddingTop + position + ((index - count) * group.size); } position += groupCount * group.size; diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 697a5102b7c..9f09c7565e4 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -1288,7 +1288,7 @@ ${formatRule(Codicon.menuSubmenu)} padding: 0 1.8em; } -.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator { +.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator, :host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator { height: 100%; mask-size: 10px 10px; diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index b20c2185169..b82b4508211 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -328,6 +328,10 @@ export class Sash extends Disposable { * The start of a vertical sash is its top-most position. */ set orthogonalStartSash(sash: Sash | undefined) { + if (this._orthogonalStartSash === sash) { + return; + } + this.orthogonalStartDragHandleDisposables.clear(); this.orthogonalStartSashDisposables.clear(); @@ -362,6 +366,10 @@ export class Sash extends Disposable { */ set orthogonalEndSash(sash: Sash | undefined) { + if (this._orthogonalEndSash === sash) { + return; + } + this.orthogonalEndDragHandleDisposables.clear(); this.orthogonalEndSashDisposables.clear(); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 5cf11ce7854..45a3b751e41 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -391,7 +391,13 @@ export abstract class AbstractScrollableElement extends Widget { let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity; if (this._options.scrollPredominantAxis) { - if (Math.abs(deltaY) >= Math.abs(deltaX)) { + if (this._options.scrollYToX && deltaX + deltaY === 0) { + // when configured to map Y to X and we both see + // no dominant axis and X and Y are competing with + // identical values into opposite directions, we + // ignore the delta as we cannot make a decision then + deltaX = deltaY = 0; + } else if (Math.abs(deltaY) >= Math.abs(deltaX)) { deltaX = 0; } else { deltaY = 0; diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index a4edaa6a3d9..b822db43751 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -259,7 +259,7 @@ abstract class ViewItem { constructor( protected container: HTMLElement, - private view: IView, + readonly view: IView, size: ViewItemSize, private disposable: IDisposable ) { @@ -280,9 +280,8 @@ abstract class ViewItem { abstract layoutContainer(offset: number): void; - dispose(): IView { + dispose(): void { this.disposable.dispose(); - return this.view; } } @@ -662,13 +661,20 @@ export class SplitView extends Disposable { if (this.areViewsDistributed()) { sizing = { type: 'distribute' }; } else { - sizing = undefined; + sizing = { type: 'split', index: sizing.index }; } } + // Save referene view, in case of `split` sizing + const referenceViewItem = sizing?.type === 'split' ? this.viewItems[sizing.index] : undefined; + // Remove view - const viewItem = this.viewItems.splice(index, 1)[0]; - const view = viewItem.dispose(); + const viewItemToRemove = this.viewItems.splice(index, 1)[0]; + + // Resize reference view, in case of `split` sizing + if (referenceViewItem) { + referenceViewItem.size += viewItemToRemove.size; + } // Remove sash if (this.viewItems.length >= 1) { @@ -684,7 +690,9 @@ export class SplitView extends Disposable { this.distributeViewSizes(); } - return view; + const result = viewItemToRemove.view; + viewItemToRemove.dispose(); + return result; } /** diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index 241c26daf23..c7a6f5e0634 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -7,6 +7,7 @@ import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { IIndexTreeModelSpliceOptions, IList } from 'vs/base/browser/ui/tree/indexTreeModel'; import { IObjectTreeModel, IObjectTreeModelOptions, IObjectTreeModelSetChildrenOptions, ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { ICollapseStateChangeEvent, IObjectTreeElement, ITreeModel, ITreeModelSpliceEvent, ITreeNode, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree'; +import { equals } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; @@ -146,7 +147,7 @@ export class CompressedObjectTreeModel, TFilterData e children: Iterable> = Iterable.empty(), options: IObjectTreeModelSetChildrenOptions, ): void { - // Diffs must be deem, since the compression can affect nested elements. + // Diffs must be deep, since the compression can affect nested elements. // @see https://github.com/microsoft/vscode/pull/114237#issuecomment-759425034 const diffIdentityProvider = options.diffIdentityProvider && wrapIdentityProvider(options.diffIdentityProvider); @@ -170,6 +171,16 @@ export class CompressedObjectTreeModel, TFilterData e const splicedElement = splice(decompressedElement, element, children); const recompressedElement = (this.enabled ? compress : noCompress)(splicedElement); + // If the recompressed node is identical to the original, just set its children. + // Saves work and churn diffing the parent element. + const elementComparator = options.diffIdentityProvider + ? ((a: T, b: T) => options.diffIdentityProvider!.getId(a) === options.diffIdentityProvider!.getId(b)) + : undefined; + if (equals(recompressedElement.element.elements, node.element.elements, elementComparator)) { + this._setChildren(compressedNode, recompressedElement.children || Iterable.empty(), { diffIdentityProvider, diffDepth: 1 }); + return; + } + const parentChildren = parent.children .map(child => child === node ? recompressedElement : child); diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 94e8650f2de..262badedf9f 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IListDragAndDrop, IListDragOverReaction, IListRenderer, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { Event } from 'vs/base/common/event'; @@ -176,7 +177,7 @@ export interface ITreeMouseEvent { export interface ITreeContextMenuEvent { readonly browserEvent: UIEvent; readonly element: T | null; - readonly anchor: HTMLElement | { readonly x: number; readonly y: number }; + readonly anchor: HTMLElement | IMouseEvent; } export interface ITreeNavigator { diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index fdbdb2e1030..2ee86a98646 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -3,6 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// ESM-comment-begin +export const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// export const isESM = true; +// ESM-uncomment-end + export abstract class LoaderStats { abstract get amdLoad(): [string, number][]; abstract get amdInvoke(): [string, number][]; @@ -41,7 +48,7 @@ export abstract class LoaderStats { } let stats: readonly LoaderEvent[] = []; - if (typeof require.getStats === 'function') { + if (typeof require === 'function' && typeof require.getStats === 'function') { stats = require.getStats().slice(0).sort((a, b) => a.timestamp - b.timestamp); } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 6137c4751f6..e00f3d59653 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -417,6 +417,7 @@ export class ThrottledDelayer { dispose(): void { this.delayer.dispose(); + this.throttler.dispose(); } } diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index ff61eb5c9e2..08736ab8c0b 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -172,53 +172,59 @@ export class VSBuffer { writeUInt8(this.buffer, value, offset); } - indexOf(subarray: VSBuffer | Uint8Array) { - const needle = subarray instanceof VSBuffer ? subarray.buffer : subarray; - const needleLen = needle.byteLength; - const haystack = this.buffer; - const haystackLen = haystack.byteLength; - - if (needleLen === 0) { - return 0; - } - - if (needleLen === 1) { - return haystack.indexOf(needle[0]); - } - - if (needleLen > haystackLen) { - return -1; - } - - // find index of the subarray using boyer-moore-horspool algorithm - const table = indexOfTable.value; - table.fill(needle.length); - for (let i = 0; i < needle.length; i++) { - table[needle[i]] = needle.length - i - 1; - } - - let i = needle.length - 1; - let j = i; - let result = -1; - while (i < haystackLen) { - if (haystack[i] === needle[j]) { - if (j === 0) { - result = i; - break; - } - - i--; - j--; - } else { - i += Math.max(needle.length - j, table[haystack[i]]); - j = needle.length - 1; - } - } - - return result; + indexOf(subarray: VSBuffer | Uint8Array, offset = 0) { + return binaryIndexOf(this.buffer, subarray instanceof VSBuffer ? subarray.buffer : subarray, offset); } } +/** + * Like String.indexOf, but works on Uint8Arrays. + * Uses the boyer-moore-horspool algorithm to be reasonably speedy. + */ +export function binaryIndexOf(haystack: Uint8Array, needle: Uint8Array, offset = 0): number { + const needleLen = needle.byteLength; + const haystackLen = haystack.byteLength; + + if (needleLen === 0) { + return 0; + } + + if (needleLen === 1) { + return haystack.indexOf(needle[0]); + } + + if (needleLen > haystackLen - offset) { + return -1; + } + + // find index of the subarray using boyer-moore-horspool algorithm + const table = indexOfTable.value; + table.fill(needle.length); + for (let i = 0; i < needle.length; i++) { + table[needle[i]] = needle.length - i - 1; + } + + let i = offset + needle.length - 1; + let j = i; + let result = -1; + while (i < haystackLen) { + if (haystack[i] === needle[j]) { + if (j === 0) { + result = i; + break; + } + + i--; + j--; + } else { + i += Math.max(needle.length - j, table[haystack[i]]); + j = needle.length - 1; + } + } + + return result; +} + export function readUInt16LE(source: Uint8Array, offset: number): number { return ( ((source[offset + 0] << 0) >>> 0) | diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index d8ee92f757e..95def40789d 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -101,4 +101,12 @@ export class SetMap { values.forEach(fn); } + + get(key: K): ReadonlySet { + const values = this.map.get(key); + if (!values) { + return new Set(); + } + return new Set(values); + } } diff --git a/src/vs/base/common/console.ts b/src/vs/base/common/console.ts index 54bfadefbae..6527f72f5e1 100644 --- a/src/vs/base/common/console.ts +++ b/src/vs/base/common/console.ts @@ -125,7 +125,7 @@ export function log(entry: IRemoteConsoleLog, label: string): void { consoleArgs = [`%c[${label}]%`, color('blue'), ...args]; } - // Stack: add to args unless already aded + // Stack: add to args unless already added if (topFrame && !isOneStringArg) { consoleArgs.push(topFrame); } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index c4f9b728259..872263f2e9e 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -682,6 +682,7 @@ export namespace Event { } }; observable.addObserver(observer); + observable.reportChanges(); return { dispose() { observable.removeObserver(observer); diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 9cefc0e56a4..10e25c9c9e3 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -60,7 +60,7 @@ export class MarkdownString implements IMarkdownString { this.value += escapeMarkdownSyntaxTokens(this.supportThemeIcons ? escapeIcons(value) : value) .replace(/([ \t]+)/g, (_match, g1) => ' '.repeat(g1.length)) .replace(/\>/gm, '\\>') - .replace(/\n/g, newlineStyle === MarkdownStringTextNewlineStyle.Break ? '\\\n' : '\n\n'); + .replace(/\n/g, newlineStyle === MarkdownStringTextNewlineStyle.Break ? '\\\n' : '\n\n'); // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. return this; } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 16333957989..897a9fd8249 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -230,10 +230,9 @@ export function filter(obj: obj, predicate: (key: string, value: any) => boolean export function getAllPropertyNames(obj: object): string[] { let res: string[] = []; - let proto = Object.getPrototypeOf(obj); - while (Object.prototype !== proto) { - res = res.concat(Object.getOwnPropertyNames(proto)); - proto = Object.getPrototypeOf(proto); + while (Object.prototype !== obj) { + res = res.concat(Object.getOwnPropertyNames(obj)); + obj = Object.getPrototypeOf(obj); } return res; } diff --git a/src/vs/base/common/observable.ts b/src/vs/base/common/observable.ts index c588d68b9f6..895ba31352d 100644 --- a/src/vs/base/common/observable.ts +++ b/src/vs/base/common/observable.ts @@ -10,19 +10,43 @@ export { ISettable, ISettableObservable, ITransaction, + IChangeContext, + IChangeTracker, observableValue, + disposableObservableValue, transaction, -} from 'vs/base/common/observableImpl/base'; -export { derived } from 'vs/base/common/observableImpl/derived'; + subtransaction, +} from 'vs/base/common/observableInternal/base'; +export { + derived, + derivedOpts, + derivedHandleChanges, + derivedWithStore, +} from 'vs/base/common/observableInternal/derived'; export { autorun, autorunDelta, autorunHandleChanges, autorunWithStore, -} from 'vs/base/common/observableImpl/autorun'; -export * from 'vs/base/common/observableImpl/utils'; + autorunOpts, + autorunWithStoreHandleChanges, +} from 'vs/base/common/observableInternal/autorun'; +export { + IObservableSignal, + constObservable, + debouncedObservable, + derivedObservableWithCache, + derivedObservableWithWritableCache, + keepAlive, + observableFromEvent, + observableFromPromise, + observableSignal, + observableSignalFromEvent, + waitForState, + wasEventTriggeredRecently, +} from 'vs/base/common/observableInternal/utils'; -import { ConsoleObservableLogger, setLogger } from 'vs/base/common/observableImpl/logging'; +import { ConsoleObservableLogger, setLogger } from 'vs/base/common/observableInternal/logging'; const enableLogging = false; if (enableLogging) { diff --git a/src/vs/base/common/observableImpl/autorun.ts b/src/vs/base/common/observableInternal/autorun.ts similarity index 75% rename from src/vs/base/common/observableImpl/autorun.ts rename to src/vs/base/common/observableInternal/autorun.ts index aca09e3f08e..482e592d883 100644 --- a/src/vs/base/common/observableImpl/autorun.ts +++ b/src/vs/base/common/observableInternal/autorun.ts @@ -5,35 +5,31 @@ import { assertFn } from 'vs/base/common/assert'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IReader, IObservable, IObserver, IChangeContext } from 'vs/base/common/observableImpl/base'; -import { getLogger } from 'vs/base/common/observableImpl/logging'; +import { IReader, IObservable, IObserver, IChangeContext, getFunctionName } from 'vs/base/common/observableInternal/base'; +import { getLogger } from 'vs/base/common/observableInternal/logging'; -export function autorun(debugName: string, fn: (reader: IReader) => void): IDisposable { - return new AutorunObserver(debugName, fn, undefined, undefined); +export function autorunOpts(options: { debugName: string | (() => string | undefined) }, fn: (reader: IReader) => void): IDisposable { + return new AutorunObserver(options.debugName, fn, undefined, undefined); +} + +export function autorun(fn: (reader: IReader) => void): IDisposable { + return new AutorunObserver(undefined, fn, undefined, undefined); } export function autorunHandleChanges( - debugName: string, options: { + debugName?: string | (() => string | undefined); createEmptyChangeSummary?: () => TChangeSummary; handleChange: (context: IChangeContext, changeSummary: TChangeSummary) => boolean; }, fn: (reader: IReader, changeSummary: TChangeSummary) => void ): IDisposable { - return new AutorunObserver(debugName, fn, options.createEmptyChangeSummary, options.handleChange); -} - -// TODO@hediet rename to autorunWithStore -export function autorunWithStore2( - debugName: string, - fn: (reader: IReader, store: DisposableStore) => void, -): IDisposable { - return autorunWithStore(fn, debugName); + return new AutorunObserver(options.debugName, fn, options.createEmptyChangeSummary, options.handleChange); } export function autorunWithStoreHandleChanges( - debugName: string, options: { + debugName?: string | (() => string | undefined); createEmptyChangeSummary?: () => TChangeSummary; handleChange: (context: IChangeContext, changeSummary: TChangeSummary) => boolean; }, @@ -41,8 +37,8 @@ export function autorunWithStoreHandleChanges( ): IDisposable { const store = new DisposableStore(); const disposable = autorunHandleChanges( - debugName, { + debugName: options.debugName ?? (() => getFunctionName(fn)), createEmptyChangeSummary: options.createEmptyChangeSummary, handleChange: options.handleChange, }, @@ -57,14 +53,12 @@ export function autorunWithStoreHandleChanges( }); } -// TODO@hediet deprecate, rename to autorunWithStoreEx -export function autorunWithStore( - fn: (reader: IReader, store: DisposableStore) => void, - debugName: string -): IDisposable { +export function autorunWithStore(fn: (reader: IReader, store: DisposableStore) => void): IDisposable { const store = new DisposableStore(); - const disposable = autorun( - debugName, + const disposable = autorunOpts( + { + debugName: () => getFunctionName(fn) || '(anonymous)', + }, reader => { store.clear(); fn(reader, store); @@ -98,9 +92,23 @@ export class AutorunObserver implements IObserver, IReader private dependenciesToBeRemoved = new Set>(); private changeSummary: TChangeSummary | undefined; + public get debugName(): string { + if (typeof this._debugName === 'string') { + return this._debugName; + } + if (typeof this._debugName === 'function') { + const name = this._debugName(); + if (name !== undefined) { return name; } + } + const name = getFunctionName(this._runFn); + if (name !== undefined) { return name; } + + return '(anonymous)'; + } + constructor( - public readonly debugName: string, - private readonly runFn: (reader: IReader, changeSummary: TChangeSummary) => void, + private readonly _debugName: string | (() => string | undefined) | undefined, + public readonly _runFn: (reader: IReader, changeSummary: TChangeSummary) => void, private readonly createChangeSummary: (() => TChangeSummary) | undefined, private readonly _handleChange: ((context: IChangeContext, summary: TChangeSummary) => boolean) | undefined, ) { @@ -128,13 +136,15 @@ export class AutorunObserver implements IObserver, IReader this.state = AutorunState.upToDate; - getLogger()?.handleAutorunTriggered(this); - try { - const changeSummary = this.changeSummary!; - this.changeSummary = this.createChangeSummary?.(); - this.runFn(this, changeSummary); + if (!this.disposed) { + getLogger()?.handleAutorunTriggered(this); + const changeSummary = this.changeSummary!; + this.changeSummary = this.createChangeSummary?.(); + this._runFn(this, changeSummary); + } } finally { + getLogger()?.handleAutorunFinished(this); // We don't want our observed observables to think that they are (not even temporarily) not being observed. // Thus, we only unsubscribe from observables that are definitely not read anymore. for (const o of this.dependenciesToBeRemoved) { @@ -215,13 +225,13 @@ export class AutorunObserver implements IObserver, IReader export namespace autorun { export const Observer = AutorunObserver; } + export function autorunDelta( - name: string, observable: IObservable, handler: (args: { lastValue: T | undefined; newValue: T }) => void ): IDisposable { let _lastValue: T | undefined; - return autorun(name, (reader) => { + return autorunOpts({ debugName: () => getFunctionName(handler) }, (reader) => { const newValue = observable.read(reader); const lastValue = _lastValue; _lastValue = newValue; diff --git a/src/vs/base/common/observableImpl/base.ts b/src/vs/base/common/observableInternal/base.ts similarity index 92% rename from src/vs/base/common/observableImpl/base.ts rename to src/vs/base/common/observableInternal/base.ts index 61addad7411..23ba8d4af19 100644 --- a/src/vs/base/common/observableImpl/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from 'vs/base/common/lifecycle'; -import type { derived } from 'vs/base/common/observableImpl/derived'; -import { getLogger } from 'vs/base/common/observableImpl/logging'; +import type { derived } from 'vs/base/common/observableInternal/derived'; +import { getLogger } from 'vs/base/common/observableInternal/logging'; /** * Represents an observable value. @@ -162,11 +162,21 @@ export abstract class ConvenientObservable implements IObservable(fn: (value: T, reader: IReader) => TNew): IObservable { return _derived( + (reader) => fn(this.read(reader), reader), () => { const name = getFunctionName(fn); - return name !== undefined ? name : `${this.debugName} (mapped)`; + if (name !== undefined) { + return name; + } + + // regexp to match `x => x.y` where x and y can be arbitrary identifiers (uses backref): + const regexp = /^\s*\(?\s*([a-zA-Z_$][a-zA-Z_$0-9]*)\s*\)?\s*=>\s*\1\.([a-zA-Z_$][a-zA-Z_$0-9]*)\s*$/; + const match = regexp.exec(fn.toString()); + if (match) { + return `${this.debugName}.${match[2]}`; + } + return `${this.debugName} (mapped)`; }, - (reader) => fn(this.read(reader), reader) ); } @@ -198,11 +208,9 @@ export abstract class BaseObservable extends ConvenientObserv export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void { const tx = new TransactionImpl(fn, getDebugName); try { - getLogger()?.handleBeginTransaction(tx); fn(tx); } finally { tx.finish(); - getLogger()?.handleEndTransaction(); } } @@ -217,13 +225,15 @@ export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransacti export class TransactionImpl implements ITransaction { private updatingObservers: { observer: IObserver; observable: IObservable }[] | null = []; - constructor(private readonly fn: Function, private readonly _getDebugName?: () => string) { } + constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) { + getLogger()?.handleBeginTransaction(this); + } public getDebugName(): string | undefined { if (this._getDebugName) { return this._getDebugName(); } - return getFunctionName(this.fn); + return getFunctionName(this._fn); } public updateObserver(observer: IObserver, observable: IObservable): void { @@ -238,6 +248,7 @@ export class TransactionImpl implements ITransaction { for (const { observer, observable } of updatingObservers) { observer.endUpdate(observable); } + getLogger()?.handleEndTransaction(); } } @@ -287,7 +298,7 @@ export class ObservableValue try { const oldValue = this._value; this._setValue(value); - getLogger()?.handleObservableChanged(this, { oldValue, newValue: value, change, didChange: true }); + getLogger()?.handleObservableChanged(this, { oldValue, newValue: value, change, didChange: true, hadValue: true }); for (const observer of this.observers) { tx.updateObserver(observer, this); diff --git a/src/vs/base/common/observableImpl/derived.ts b/src/vs/base/common/observableInternal/derived.ts similarity index 81% rename from src/vs/base/common/observableImpl/derived.ts rename to src/vs/base/common/observableInternal/derived.ts index 0bfe5d71681..2cb17f2f9da 100644 --- a/src/vs/base/common/observableImpl/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -5,11 +5,18 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IReader, IObservable, BaseObservable, IObserver, _setDerived, IChangeContext } from 'vs/base/common/observableImpl/base'; -import { getLogger } from 'vs/base/common/observableImpl/logging'; +import { IReader, IObservable, BaseObservable, IObserver, _setDerived, IChangeContext, getFunctionName } from 'vs/base/common/observableInternal/base'; +import { getLogger } from 'vs/base/common/observableInternal/logging'; -export function derived(debugName: string | (() => string), computeFn: (reader: IReader) => T): IObservable { - return new Derived(debugName, computeFn, undefined, undefined, undefined); +export type EqualityComparer = (a: T, b: T) => boolean; +const defaultEqualityComparer: EqualityComparer = (a, b) => a === b; + +export function derived(computeFn: (reader: IReader) => T, debugName?: string | (() => string)): IObservable { + return new Derived(debugName, computeFn, undefined, undefined, undefined, defaultEqualityComparer); +} + +export function derivedOpts(options: { debugName?: string | (() => string); equalityComparer?: EqualityComparer }, computeFn: (reader: IReader) => T): IObservable { + return new Derived(options.debugName, computeFn, undefined, undefined, undefined, options.equalityComparer ?? defaultEqualityComparer); } export function derivedHandleChanges( @@ -19,7 +26,7 @@ export function derivedHandleChanges( handleChange: (context: IChangeContext, changeSummary: TChangeSummary) => boolean; }, computeFn: (reader: IReader, changeSummary: TChangeSummary) => T): IObservable { - return new Derived(debugName, computeFn, options.createEmptyChangeSummary, options.handleChange, undefined); + return new Derived(debugName, computeFn, options.createEmptyChangeSummary, options.handleChange, undefined, defaultEqualityComparer); } export function derivedWithStore(name: string, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable { @@ -27,7 +34,7 @@ export function derivedWithStore(name: string, computeFn: (reader: IReader, s return new Derived(name, r => { store.clear(); return computeFn(r, store); - }, undefined, undefined, () => store.dispose()); + }, undefined, undefined, () => store.dispose(), defaultEqualityComparer); } _setDerived(derived); @@ -63,15 +70,19 @@ export class Derived extends BaseObservable im private changeSummary: TChangeSummary | undefined = undefined; public override get debugName(): string { + if (!this._debugName) { + return getFunctionName(this._computeFn) || '(anonymous)'; + } return typeof this._debugName === 'function' ? this._debugName() : this._debugName; } constructor( - private readonly _debugName: string | (() => string), - private readonly computeFn: (reader: IReader, changeSummary: TChangeSummary) => T, + private readonly _debugName: string | (() => string) | undefined, + public readonly _computeFn: (reader: IReader, changeSummary: TChangeSummary) => T, private readonly createChangeSummary: (() => TChangeSummary) | undefined, private readonly _handleChange: ((context: IChangeContext, summary: TChangeSummary) => boolean) | undefined, - private readonly _handleLastObserverRemoved: (() => void) | undefined = undefined + private readonly _handleLastObserverRemoved: (() => void) | undefined = undefined, + private readonly _equalityComparator: EqualityComparer, ) { super(); this.changeSummary = this.createChangeSummary?.(); @@ -97,19 +108,17 @@ export class Derived extends BaseObservable im if (this.observers.size === 0) { // Without observers, we don't know when to clean up stuff. // Thus, we don't cache anything to prevent memory leaks. - const result = this.computeFn(this, this.createChangeSummary?.()!); + const result = this._computeFn(this, this.createChangeSummary?.()!); // Clear new dependencies this.onLastObserverRemoved(); return result; } else { do { + // We might not get a notification for a dependency that changed while it is updating, + // thus we also have to ask all our depedencies if they changed in this case. if (this.state === DerivedState.dependenciesMightHaveChanged) { - // We might not get a notification for a dependency that changed while it is updating, - // thus we also have to ask all our depedencies if they changed in this case. - this.state = DerivedState.upToDate; - for (const d of this.dependencies) { - /** might call {@link handleChange} indirectly, which could invalidate us */ + /** might call {@link handleChange} indirectly, which could make us stale */ d.reportChanges(); if (this.state as DerivedState === DerivedState.stale) { @@ -119,6 +128,12 @@ export class Derived extends BaseObservable im } } + // We called report changes of all dependencies. + // If we are still not stale, we can assume to be up to date again. + if (this.state === DerivedState.dependenciesMightHaveChanged) { + this.state = DerivedState.upToDate; + } + this._recomputeIfNeeded(); // In case recomputation changed one of our dependencies, we need to recompute again. } while (this.state !== DerivedState.upToDate); @@ -142,7 +157,7 @@ export class Derived extends BaseObservable im this.changeSummary = this.createChangeSummary?.(); try { /** might call {@link handleChange} indirectly, which could invalidate us */ - this.value = this.computeFn(this, changeSummary); + this.value = this._computeFn(this, changeSummary); } finally { // We don't want our observed observables to think that they are (not even temporarily) not being observed. // Thus, we only unsubscribe from observables that are definitely not read anymore. @@ -152,13 +167,14 @@ export class Derived extends BaseObservable im this.dependenciesToBeRemoved.clear(); } - const didChange = hadValue && oldValue !== this.value; + const didChange = hadValue && !(this._equalityComparator(oldValue!, this.value)); getLogger()?.handleDerivedRecomputed(this, { oldValue, newValue: this.value, change: undefined, - didChange + didChange, + hadValue, }); if (didChange) { diff --git a/src/vs/base/common/observableImpl/logging.ts b/src/vs/base/common/observableInternal/logging.ts similarity index 85% rename from src/vs/base/common/observableImpl/logging.ts rename to src/vs/base/common/observableInternal/logging.ts index 0c221d0c700..01fcc3cdbbf 100644 --- a/src/vs/base/common/observableImpl/logging.ts +++ b/src/vs/base/common/observableInternal/logging.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AutorunObserver } from 'vs/base/common/observableImpl/autorun'; -import { IObservable, ObservableValue, TransactionImpl } from 'vs/base/common/observableImpl/base'; -import { Derived } from 'vs/base/common/observableImpl/derived'; -import { FromEventObservable } from 'vs/base/common/observableImpl/utils'; +import { AutorunObserver } from 'vs/base/common/observableInternal/autorun'; +import { IObservable, ObservableValue, TransactionImpl } from 'vs/base/common/observableInternal/base'; +import { Derived } from 'vs/base/common/observableInternal/derived'; +import { FromEventObservable } from 'vs/base/common/observableInternal/utils'; let globalObservableLogger: IObservableLogger | undefined; @@ -23,17 +23,19 @@ interface IChangeInformation { newValue: unknown; change: unknown; didChange: boolean; + hadValue: boolean; } export interface IObservableLogger { - handleObservableChanged(observable: ObservableValue, info: IChangeInformation): void; + handleObservableChanged(observable: ObservableValue, info: IChangeInformation): void; handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void; handleAutorunCreated(autorun: AutorunObserver): void; handleAutorunTriggered(autorun: AutorunObserver): void; + handleAutorunFinished(autorun: AutorunObserver): void; - handleDerivedCreated(observable: Derived): void; - handleDerivedRecomputed(observable: Derived, info: IChangeInformation): void; + handleDerivedCreated(observable: Derived): void; + handleDerivedRecomputed(observable: Derived, info: IChangeInformation): void; handleBeginTransaction(transaction: TransactionImpl): void; handleEndTransaction(): void; @@ -50,6 +52,15 @@ export class ConsoleObservableLogger implements IObservableLogger { } private formatInfo(info: IChangeInformation): ConsoleText[] { + if (!info.hadValue) { + return [ + normalText(` `), + styled(formatValue(info.newValue, 60), { + color: 'green', + }), + normalText(` (initial)`), + ]; + } return info.didChange ? [ normalText(` `), @@ -102,7 +113,8 @@ export class ConsoleObservableLogger implements IObservableLogger { formatKind('derived recomputed'), styled(derived.debugName, { color: 'BlueViolet' }), ...this.formatInfo(info), - this.formatChanges(changedObservables) + this.formatChanges(changedObservables), + { data: [{ fn: derived._computeFn }] } ])); changedObservables.clear(); } @@ -112,6 +124,7 @@ export class ConsoleObservableLogger implements IObservableLogger { formatKind('observable from event triggered'), styled(observable.debugName, { color: 'BlueViolet' }), ...this.formatInfo(info), + { data: [{ fn: observable._getValue }] } ])); } @@ -129,9 +142,15 @@ export class ConsoleObservableLogger implements IObservableLogger { console.log(...this.textToConsoleArgs([ formatKind('autorun'), styled(autorun.debugName, { color: 'BlueViolet' }), - this.formatChanges(changedObservables) + this.formatChanges(changedObservables), + { data: [{ fn: autorun._runFn }] } ])); changedObservables.clear(); + this.indentation++; + } + + handleAutorunFinished(autorun: AutorunObserver): void { + this.indentation--; } handleBeginTransaction(transaction: TransactionImpl): void { @@ -142,6 +161,7 @@ export class ConsoleObservableLogger implements IObservableLogger { console.log(...this.textToConsoleArgs([ formatKind('transaction'), styled(transactionName, { color: 'BlueViolet' }), + { data: [{ fn: transaction._fn }] } ])); this.indentation++; } @@ -153,13 +173,12 @@ export class ConsoleObservableLogger implements IObservableLogger { type ConsoleText = | (ConsoleText | undefined)[] - | { text: string; style: string; data?: Record } - | { data: Record }; + | { text: string; style: string; data?: unknown[] } + | { data: unknown[] }; function consoleTextToArgs(text: ConsoleText): unknown[] { const styles = new Array(); - const initial = {}; - const data = initial; + const data: unknown[] = []; let firstArg = ''; function process(t: ConsoleText): void { @@ -173,20 +192,17 @@ function consoleTextToArgs(text: ConsoleText): unknown[] { firstArg += `%c${t.text}`; styles.push(t.style); if (t.data) { - Object.assign(data, t.data); + data.push(...t.data); } } else if ('data' in t) { - Object.assign(data, t.data); + data.push(...t.data); } } process(text); const result = [firstArg, ...styles]; - if (Object.keys(data).length > 0) { - result.push(data); - } - + result.push(...data); return result; } diff --git a/src/vs/base/common/observableImpl/utils.ts b/src/vs/base/common/observableInternal/utils.ts similarity index 89% rename from src/vs/base/common/observableImpl/utils.ts rename to src/vs/base/common/observableInternal/utils.ts index 5d9a2c568a8..c0e0adba010 100644 --- a/src/vs/base/common/observableImpl/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -5,11 +5,14 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { autorun } from 'vs/base/common/observableImpl/autorun'; -import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, getFunctionName, observableValue, transaction } from 'vs/base/common/observableImpl/base'; -import { derived } from 'vs/base/common/observableImpl/derived'; -import { getLogger } from 'vs/base/common/observableImpl/logging'; +import { autorun } from 'vs/base/common/observableInternal/autorun'; +import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, getFunctionName, observableValue, transaction } from 'vs/base/common/observableInternal/base'; +import { derived } from 'vs/base/common/observableInternal/derived'; +import { getLogger } from 'vs/base/common/observableInternal/logging'; +/** + * Represents an efficient observable whose value never changes. + */ export function constObservable(value: T): IObservable { return new ConstObservable(value); } @@ -53,7 +56,8 @@ export function waitForState(observable: IObservable, predicate: (state: T return new Promise(resolve => { let didRun = false; let shouldDispose = false; - const d = autorun('waitForState', reader => { + const d = autorun(reader => { + /** @description waitForState */ const currentState = observable.read(reader); if (predicate(currentState)) { if (!didRun) { @@ -85,13 +89,13 @@ export class FromEventObservable extends BaseObservable { constructor( private readonly event: Event, - private readonly getValue: (args: TArgs | undefined) => T + public readonly _getValue: (args: TArgs | undefined) => T ) { super(); } private getDebugName(): string | undefined { - return getFunctionName(this.getValue); + return getFunctionName(this._getValue); } public get debugName(): string { @@ -104,11 +108,11 @@ export class FromEventObservable extends BaseObservable { } private readonly handleEvent = (args: TArgs | undefined) => { - const newValue = this.getValue(args); + const newValue = this._getValue(args); const didChange = !this.hasValue || this.value !== newValue; - getLogger()?.handleFromEventObservableTriggered(this, { oldValue: this.value, newValue, change: undefined, didChange }); + getLogger()?.handleFromEventObservableTriggered(this, { oldValue: this.value, newValue, change: undefined, didChange, hadValue: this.hasValue }); if (didChange) { this.value = newValue; @@ -146,7 +150,7 @@ export class FromEventObservable extends BaseObservable { return this.value!; } else { // no cache, as there are no subscribers to keep it updated - return this.getValue(undefined); + return this._getValue(undefined); } } } @@ -200,6 +204,8 @@ class FromEventObservableSignal extends BaseObservable { /** * Creates a signal that can be triggered to invalidate observers. + * Signals don't have a value - when they are triggered they indicate a change. + * However, signals can carry a delta that is passed to observers. */ export function observableSignal( debugName: string @@ -242,7 +248,8 @@ export function debouncedObservable(observable: IObservable, debounceMs: n let timeout: any = undefined; - disposableStore.add(autorun('debounce', reader => { + disposableStore.add(autorun(reader => { + /** @description debounce */ const value = observable.read(reader); if (timeout) { @@ -278,6 +285,7 @@ export function wasEventTriggeredRecently(event: Event, timeoutMs: number, return observable; } +// TODO@hediet: Have `keepCacheAlive` and `recomputeOnChange` instead of forceRecompute /** * This ensures the observable is being observed. * Observed observables (such as {@link derived}s) can maintain a cache, as they receive invalidation events. @@ -326,21 +334,21 @@ class KeepAliveObserver implements IObserver { export function derivedObservableWithCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable { let lastValue: T | undefined = undefined; - const observable = derived(name, reader => { + const observable = derived(reader => { lastValue = computeFn(reader, lastValue); return lastValue; - }); + }, name); return observable; } export function derivedObservableWithWritableCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable & { clearCache(transaction: ITransaction): void } { let lastValue: T | undefined = undefined; const counter = observableValue('derivedObservableWithWritableCache.counter', 0); - const observable = derived(name, reader => { + const observable = derived(reader => { counter.read(reader); lastValue = computeFn(reader, lastValue); return lastValue; - }); + }, name); return Object.assign(observable, { clearCache: (transaction: ITransaction) => { lastValue = undefined; diff --git a/src/vs/base/common/prefixTree.ts b/src/vs/base/common/prefixTree.ts new file mode 100644 index 00000000000..20ef79000e2 --- /dev/null +++ b/src/vs/base/common/prefixTree.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const unset = Symbol('unset'); + +/** + * A simple prefix tree implementation where a value is stored based on + * well-defined prefix segments. + */ +export class WellDefinedPrefixTree { + private readonly root = new Node(); + private _size = 0; + + public get size() { + return this._size; + } + + /** Inserts a new value in the prefix tree. */ + insert(key: Iterable, value: V): void { + this.opNode(key, n => n.value = value); + } + + /** Mutates a value in the prefix tree. */ + mutate(key: Iterable, mutate: (value?: V) => V): void { + this.opNode(key, n => n.value = mutate(n.value === unset ? undefined : n.value)); + } + + /** Deletes a node from the prefix tree, returning the value it contained. */ + delete(key: Iterable): V | undefined { + const path = [{ part: '', node: this.root }]; + let i = 0; + for (const part of key) { + const node = path[i].node.children?.get(part); + if (!node) { + return undefined; // node not in tree + } + + path.push({ part, node }); + i++; + } + + const value = path[i].node.value; + if (value === unset) { + return; // not actually a real node + } + + this._size--; + for (; i > 0; i--) { + const parent = path[i - 1]; + parent.node.children!.delete(path[i].part); + if (parent.node.children!.size > 0 || parent.node.value !== unset) { + break; + } + } + + return value; + } + + /** Gets a value from the tree. */ + find(key: Iterable): V | undefined { + let node = this.root; + for (const segment of key) { + const next = node.children?.get(segment); + if (!next) { + return undefined; + } + + node = next; + } + + return node.value === unset ? undefined : node.value; + } + + /** Gets whether the tree has the key, or a parent of the key, already inserted. */ + hasKeyOrParent(key: Iterable): boolean { + let node = this.root; + for (const segment of key) { + const next = node.children?.get(segment); + if (!next) { + return false; + } + if (next.value !== unset) { + return true; + } + + node = next; + } + + return false; + } + + /** Gets whether the tree has the given key or any children. */ + hasKeyOrChildren(key: Iterable): boolean { + let node = this.root; + for (const segment of key) { + const next = node.children?.get(segment); + if (!next) { + return false; + } + + node = next; + } + + return true; + } + + /** Gets whether the tree has the given key. */ + hasKey(key: Iterable): boolean { + let node = this.root; + for (const segment of key) { + const next = node.children?.get(segment); + if (!next) { + return false; + } + + node = next; + } + + return node.value !== unset; + } + + private opNode(key: Iterable, fn: (node: Node) => void): void { + let node = this.root; + for (const part of key) { + if (!node.children) { + const next = new Node(); + node.children = new Map([[part, next]]); + node = next; + } else if (!node.children.has(part)) { + const next = new Node(); + node.children.set(part, next); + node = next; + } else { + node = node.children.get(part)!; + } + } + + if (node.value === unset) { + this._size++; + } + + fn(node); + } + + /** Returns an iterable of the tree values in no defined order. */ + *values() { + const stack = [this.root]; + while (stack.length > 0) { + const node = stack.pop()!; + if (node.value !== unset) { + yield node.value; + } + + if (node.children) { + for (const child of node.children.values()) { + stack.push(child); + } + } + } + } +} + +class Node { + public children?: Map>; + public value: T | typeof unset = unset; +} diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 7cc06b49b3f..47b2de558c4 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -130,11 +130,6 @@ export interface IProductConfiguration { readonly ariaKey: string; }; - readonly sendASmile?: { - readonly reportIssueUrl: string; - readonly requestFeatureUrl: string; - }; - readonly documentationUrl?: string; readonly serverDocumentationUrl?: string; readonly releaseNotesUrl?: string; @@ -144,7 +139,7 @@ export interface IProductConfiguration { readonly introductoryVideosUrl?: string; readonly tipsAndTricksUrl?: string; readonly newsletterSignupUrl?: string; - readonly twitterUrl?: string; + readonly youTubeUrl?: string; readonly requestFeatureUrl?: string; readonly reportIssueUrl?: string; readonly reportMarketplaceIssueUrl?: string; diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index c35da047d1f..c66ad018097 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -225,6 +225,10 @@ export type AddFirstParameterToFunctions }> = Partial & U[keyof U]; +/** + * Only picks the non-optional properties of a type. + */ +export type OmitOptional = { [K in keyof T as T[K] extends Required[K] ? K : never]: T[K] }; /** * A type that removed readonly-less from all properties of `T` diff --git a/src/vs/base/node/nodeStreams.ts b/src/vs/base/node/nodeStreams.ts new file mode 100644 index 00000000000..0719bb46787 --- /dev/null +++ b/src/vs/base/node/nodeStreams.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Transform } from 'stream'; +import { binaryIndexOf } from 'vs/base/common/buffer'; + +/** + * A Transform stream that splits the input on the "splitter" substring. + * The resulting chunks will contain (and trail with) the splitter match. + * The last chunk when the stream ends will be emitted even if a splitter + * is not encountered. + */ +export class StreamSplitter extends Transform { + private buffer: Buffer | undefined; + private readonly splitter: Buffer | number; + private readonly spitterLen: number; + + constructor(splitter: string | number | Buffer) { + super(); + if (typeof splitter === 'number') { + this.splitter = splitter; + this.spitterLen = 1; + } else { + const buf = Buffer.isBuffer(splitter) ? splitter : Buffer.from(splitter); + this.splitter = buf.length === 1 ? buf[0] : buf; + this.spitterLen = buf.length; + } + } + + override _transform(chunk: Buffer, _encoding: string, callback: (error?: Error | null, data?: any) => void): void { + if (!this.buffer) { + this.buffer = chunk; + } else { + this.buffer = Buffer.concat([this.buffer, chunk]); + } + + let offset = 0; + while (offset < this.buffer.length) { + const index = typeof this.splitter === 'number' + ? this.buffer.indexOf(this.splitter, offset) + : binaryIndexOf(this.buffer, this.splitter, offset); + if (index === -1) { + break; + } + + this.push(this.buffer.slice(offset, index + this.spitterLen)); + offset = index + this.spitterLen; + } + + this.buffer = offset === this.buffer.length ? undefined : this.buffer.slice(offset); + callback(); + } + + override _flush(callback: (error?: Error | null, data?: any) => void): void { + if (this.buffer) { + this.push(this.buffer); + } + + callback(); + } +} diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 0cdb1809b52..9e409829cae 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import { tmpdir } from 'os'; import { promisify } from 'util'; -import { ResourceQueue } from 'vs/base/common/async'; +import { ResourceQueue, timeout } from 'vs/base/common/async'; import { isEqualOrParent, isRootOrDriveLetter, randomPath } from 'vs/base/common/extpath'; import { normalizeNFC } from 'vs/base/common/normalization'; import { join } from 'vs/base/common/path'; @@ -491,16 +491,24 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio /** * A drop-in replacement for `fs.rename` that: * - allows to move across multiple disks + * - attempts to retry the operation for certain error codes on Windows */ -async function move(source: string, target: string): Promise { +async function rename(source: string, target: string, windowsRetryTimeout: number | false = 60000 /* matches graceful-fs */): Promise { if (source === target) { return; // simulate node.js behaviour here and do a no-op if paths match } try { - await Promises.rename(source, target); + if (isWindows && typeof windowsRetryTimeout === 'number') { + // On Windows, a rename can fail when either source or target + // is locked by AV software. We do leverage graceful-fs to iron + // out these issues, however in case the target file exists, + // graceful-fs will immediately return without retry for fs.rename(). + await renameWithRetry(source, target, Date.now(), windowsRetryTimeout); + } else { + await promisify(fs.rename)(source, target); + } } catch (error) { - // In two cases we fallback to classic copy and delete: // // 1.) The EXDEV error indicates that source and target are on different devices @@ -518,6 +526,44 @@ async function move(source: string, target: string): Promise { } } +async function renameWithRetry(source: string, target: string, startTime: number, retryTimeout: number, attempt = 0): Promise { + try { + return await promisify(fs.rename)(source, target); + } catch (error) { + if (error.code !== 'EACCES' && error.code !== 'EPERM' && error.code !== 'EBUSY') { + throw error; // only for errors we think are temporary + } + + if (Date.now() - startTime >= retryTimeout) { + console.error(`[node.js fs] rename failed after ${attempt} retries with error: ${error}`); + + throw error; // give up after configurable timeout + } + + if (attempt === 0) { + let abortRetry = false; + try { + const { stat } = await SymlinkSupport.stat(target); + if (!stat.isFile()) { + abortRetry = true; // if target is not a file, EPERM error may be raised and we should not attempt to retry + } + } catch (error) { + // Ignore + } + + if (abortRetry) { + throw error; + } + } + + // Delay with incremental backoff up to 100ms + await timeout(Math.min(100, attempt * 10)); + + // Attempt again + return renameWithRetry(source, target, startTime, retryTimeout, attempt + 1); + } +} + interface ICopyPayload { readonly root: { source: string; target: string }; readonly options: { preserveSymlinks: boolean }; @@ -694,7 +740,6 @@ export const Promises = new class { get fdatasync() { return promisify(fs.fdatasync); } get truncate() { return promisify(fs.truncate); } - get rename() { return promisify(fs.rename); } get copyFile() { return promisify(fs.copyFile); } get open() { return promisify(fs.open); } @@ -733,7 +778,7 @@ export const Promises = new class { get rm() { return rimraf; } - get move() { return move; } + get rename() { return rename; } get copy() { return copy; } //#endregion diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 171b9e45bbc..767d0a5aec6 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -36,7 +36,6 @@ export type ExtractErrorType = 'CorruptZip' | 'Incomplete'; export class ExtractError extends Error { readonly type?: ExtractErrorType; - readonly cause: Error; constructor(type: ExtractErrorType | undefined, cause: Error) { let message = cause.message; diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index d71baaeeeb5..747e8513b1f 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -8,7 +8,7 @@ import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/com import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { memoize } from 'vs/base/common/decorators'; -import { CancellationError } from 'vs/base/common/errors'; +import { CancellationError, ErrorNoTelemetry } from 'vs/base/common/errors'; import { Emitter, Event, EventMultiplexer, Relay } from 'vs/base/common/event'; import { combinedDisposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; @@ -1102,7 +1102,7 @@ export namespace ProxyChannel { } } - throw new Error(`Event not found: ${event}`); + throw new ErrorNoTelemetry(`Event not found: ${event}`); } call(_: unknown, command: string, args?: any[]): Promise { @@ -1119,7 +1119,7 @@ export namespace ProxyChannel { return target.apply(handler, args); } - throw new Error(`Method not found: ${command}`); + throw new ErrorNoTelemetry(`Method not found: ${command}`); } }; } @@ -1185,7 +1185,7 @@ export namespace ProxyChannel { }; } - throw new Error(`Property not found: ${String(propKey)}`); + throw new ErrorNoTelemetry(`Property not found: ${String(propKey)}`); } }) as T; } diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index f4cf5ac78e1..e629ab01f0e 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -274,7 +274,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { try { await Promises.unlink(path); try { - await Promises.rename(this.toBackupPath(path), path); + await Promises.rename(this.toBackupPath(path), path, false /* no retry */); } catch (error) { // ignore } diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 3b451445a63..518b250a943 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -343,3 +343,83 @@ suite('RangeMap', () => { }); }); }); + +suite('RangeMap with top padding', () => { + let rangeMap: RangeMap; + + setup(() => { + rangeMap = new RangeMap(10); + }); + + test('empty', () => { + assert.strictEqual(rangeMap.size, 10); + assert.strictEqual(rangeMap.count, 0); + }); + + const one = { size: 1 }; + const five = { size: 5 }; + const ten = { size: 10 }; + + test('length & count', () => { + rangeMap.splice(0, 0, [one]); + assert.strictEqual(rangeMap.size, 11); + assert.strictEqual(rangeMap.count, 1); + }); + + test('length & count #2', () => { + rangeMap.splice(0, 0, [one, one, one, one, one]); + assert.strictEqual(rangeMap.size, 15); + assert.strictEqual(rangeMap.count, 5); + }); + + test('length & count #3', () => { + rangeMap.splice(0, 0, [five]); + assert.strictEqual(rangeMap.size, 15); + assert.strictEqual(rangeMap.count, 1); + }); + + test('length & count #4', () => { + rangeMap.splice(0, 0, [five, five, five, five, five]); + assert.strictEqual(rangeMap.size, 35); + assert.strictEqual(rangeMap.count, 5); + }); + + test('insert', () => { + rangeMap.splice(0, 0, [five, five, five, five, five]); + assert.strictEqual(rangeMap.size, 35); + assert.strictEqual(rangeMap.count, 5); + + rangeMap.splice(0, 0, [five, five, five, five, five]); + assert.strictEqual(rangeMap.size, 60); + assert.strictEqual(rangeMap.count, 10); + + rangeMap.splice(5, 0, [ten, ten]); + assert.strictEqual(rangeMap.size, 80); + assert.strictEqual(rangeMap.count, 12); + + rangeMap.splice(12, 0, [{ size: 200 }]); + assert.strictEqual(rangeMap.size, 280); + assert.strictEqual(rangeMap.count, 13); + }); + + suite('indexAt, positionAt', () => { + test('empty', () => { + assert.strictEqual(rangeMap.indexAt(0), 0); + assert.strictEqual(rangeMap.indexAt(10), 0); + assert.strictEqual(rangeMap.indexAt(-1), -1); + assert.strictEqual(rangeMap.positionAt(0), -1); + assert.strictEqual(rangeMap.positionAt(10), -1); + assert.strictEqual(rangeMap.positionAt(-1), -1); + }); + + test('simple', () => { + rangeMap.splice(0, 0, [one]); + assert.strictEqual(rangeMap.indexAt(0), 0); + assert.strictEqual(rangeMap.indexAt(1), 0); + assert.strictEqual(rangeMap.indexAt(10), 0); + assert.strictEqual(rangeMap.indexAt(11), 1); + assert.strictEqual(rangeMap.positionAt(0), 10); + assert.strictEqual(rangeMap.positionAt(1), -1); + }); + }); +}); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 2d871251d7c..144c119389a 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -255,6 +255,12 @@ suite('Async', () => { // OK } }); + + test('trigger after dispose throws', async () => { + const throttledDelayer = new async.ThrottledDelayer(100); + throttledDelayer.dispose(); + await assert.rejects(() => throttledDelayer.trigger(async () => { }, 0)); + }); }); test('simple cancel', function () { diff --git a/src/vs/base/test/common/filters.perf.test.ts b/src/vs/base/test/common/filters.perf.test.ts index 8ac5a3c89d9..fd311ecd6f7 100644 --- a/src/vs/base/test/common/filters.perf.test.ts +++ b/src/vs/base/test/common/filters.perf.test.ts @@ -2,8 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import * as filters from 'vs/base/common/filters'; -import { data } from 'vs/base/test/common/filters.perf.data'; +import { FileAccess } from 'vs/base/common/network'; const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons', 'zyx', 'aBc']; @@ -15,7 +16,10 @@ function perfSuite(name: string, callback: (this: Mocha.Suite) => void) { } } -perfSuite('Performance - fuzzyMatch', function () { +perfSuite('Performance - fuzzyMatch', async function () { + + const uri = FileAccess.asBrowserUri('vs/base/test/common/filters.perf.data').toString(true); + const { data } = await importAMDNodeModule(uri, ''); // suiteSetup(() => console.profile()); // suiteTeardown(() => console.profileEnd()); @@ -47,7 +51,10 @@ perfSuite('Performance - fuzzyMatch', function () { }); -perfSuite('Performance - IFilter', function () { +perfSuite('Performance - IFilter', async function () { + + const uri = FileAccess.asBrowserUri('vs/base/test/common/filters.perf.data').toString(true); + const { data } = await importAMDNodeModule(uri, ''); function perfTest(name: string, match: filters.IFilter) { test(name, () => { diff --git a/src/vs/base/test/common/observable.test.ts b/src/vs/base/test/common/observable.test.ts index 64d08a6c4d5..4f507c7ece7 100644 --- a/src/vs/base/test/common/observable.test.ts +++ b/src/vs/base/test/common/observable.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { ISettableObservable, autorun, derived, ITransaction, observableFromEvent, observableValue, transaction, keepAlive } from 'vs/base/common/observable'; -import { BaseObservable, IObservable, IObserver } from 'vs/base/common/observableImpl/base'; +import { BaseObservable, IObservable, IObserver } from 'vs/base/common/observableInternal/base'; suite('observables', () => { /** @@ -17,7 +17,8 @@ suite('observables', () => { const log = new Log(); const myObservable = observableValue('myObservable', 0); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun.run(myObservable: ${myObservable.read(reader)})`); }); // The autorun runs immediately @@ -49,7 +50,8 @@ suite('observables', () => { const observable1 = observableValue('myObservable1', 0); const observable2 = observableValue('myObservable2', 0); - const myDerived = derived('myDerived', (reader) => { + const myDerived = derived(reader => { + /** @description myDerived */ const value1 = observable1.read(reader); const value2 = observable2.read(reader); const sum = value1 + value2; @@ -57,7 +59,8 @@ suite('observables', () => { return sum; }); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun(myDerived: ${myDerived.read(reader)})`); }); // autorun runs immediately @@ -110,7 +113,8 @@ suite('observables', () => { const observable1 = observableValue('myObservable1', 0); const observable2 = observableValue('myObservable2', 0); - const myDerived = derived('myDerived', (reader) => { + const myDerived = derived((reader) => { + /** @description myDerived */ const value1 = observable1.read(reader); const value2 = observable2.read(reader); const sum = value1 + value2; @@ -118,7 +122,8 @@ suite('observables', () => { return sum; }); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun(myDerived: ${myDerived.read(reader)})`); }); // autorun runs immediately @@ -147,25 +152,29 @@ suite('observables', () => { test('get without observers', () => { const log = new Log(); const observable1 = observableValue('myObservableValue1', 0); - const computed1 = derived('computed', (reader) => { + const computed1 = derived((reader) => { + /** @description computed */ const value1 = observable1.read(reader); const result = value1 % 3; log.log(`recompute1: ${value1} % 3 = ${result}`); return result; }); - const computed2 = derived('computed', (reader) => { + const computed2 = derived((reader) => { + /** @description computed */ const value1 = computed1.read(reader); const result = value1 * 2; log.log(`recompute2: ${value1} * 2 = ${result}`); return result; }); - const computed3 = derived('computed', (reader) => { + const computed3 = derived((reader) => { + /** @description computed */ const value1 = computed1.read(reader); const result = value1 * 3; log.log(`recompute3: ${value1} * 3 = ${result}`); return result; }); - const computedSum = derived('computed', (reader) => { + const computedSum = derived((reader) => { + /** @description computed */ const value1 = computed2.read(reader); const value2 = computed3.read(reader); const result = value1 + value2; @@ -256,7 +265,8 @@ suite('observables', () => { const myObservable1 = observableValue('myObservable1', 0); const myObservable2 = observableValue('myObservable2', 0); - const myComputed1 = derived('myComputed1', (reader) => { + const myComputed1 = derived(reader => { + /** @description myComputed1 */ const value1 = myObservable1.read(reader); const value2 = myObservable2.read(reader); const sum = value1 + value2; @@ -264,7 +274,8 @@ suite('observables', () => { return sum; }); - const myComputed2 = derived('myComputed2', (reader) => { + const myComputed2 = derived(reader => { + /** @description myComputed2 */ const value1 = myComputed1.read(reader); const value2 = myObservable1.read(reader); const value3 = myObservable2.read(reader); @@ -273,7 +284,8 @@ suite('observables', () => { return sum; }); - const myComputed3 = derived('myComputed3', (reader) => { + const myComputed3 = derived(reader => { + /** @description myComputed3 */ const value1 = myComputed2.read(reader); const value2 = myObservable1.read(reader); const value3 = myObservable2.read(reader); @@ -282,7 +294,8 @@ suite('observables', () => { return sum; }); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun.run(myComputed3: ${myComputed3.read(reader)})`); }); assert.deepStrictEqual(log.getAndClearEntries(), [ @@ -364,7 +377,8 @@ suite('observables', () => { setValue(undefined); - const autorunDisposable = autorun('MyAutorun', (reader) => { + const autorunDisposable = autorun(reader => { + /** @description MyAutorun */ observable.read(reader); log.log( `autorun, value: ${observable.read(reader)}` @@ -396,7 +410,8 @@ suite('observables', () => { const shouldReadObservable = observableValue('shouldReadObservable', true); - const autorunDisposable = autorun('MyAutorun', (reader) => { + const autorunDisposable = autorun(reader => { + /** @description MyAutorun */ if (shouldReadObservable.read(reader)) { observable.read(reader); log.log( @@ -468,14 +483,16 @@ suite('observables', () => { const shouldReadObservable = observableValue('shouldReadMyObs1', true); const myObs1 = new LoggingObservableValue('myObs1', 0, log); - const myComputed = derived('myComputed', reader => { + const myComputed = derived(reader => { + /** @description myComputed */ log.log('myComputed.recompute'); if (shouldReadObservable.read(reader)) { return myObs1.read(reader); } return 1; }); - autorun('myAutorun', reader => { + autorun(reader => { + /** @description myAutorun */ const value = myComputed.read(reader); log.log(`myAutorun: ${value}`); }); @@ -508,14 +525,16 @@ suite('observables', () => { const myObsShouldRead = new LoggingObservableValue('myObsShouldRead', true, log); const myObs1 = new LoggingObservableValue('myObs1', 0, log); - const myComputed1 = derived('myComputed1', reader => { + const myComputed1 = derived(reader => { + /** @description myComputed1 */ const myObs1Val = myObs1.read(reader); const result = myObs1Val % 10; log.log(`myComputed1(myObs1: ${myObs1Val}): Computed ${result}`); return myObs1Val; }); - autorun('myAutorun', reader => { + autorun(reader => { + /** @description myAutorun */ const shouldRead = myObsShouldRead.read(reader); if (shouldRead) { const v = myComputed1.read(reader); @@ -568,7 +587,8 @@ suite('observables', () => { const log = new Log(); const myObservable = observableValue('myObservable', 0); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun.run(myObservable: ${myObservable.read(reader)})`); }); assert.deepStrictEqual(log.getAndClearEntries(), ['myAutorun.run(myObservable: 0)']); @@ -587,13 +607,15 @@ suite('observables', () => { test('autorun does not rerun on indirect neutral observable double change', () => { const log = new Log(); const myObservable = observableValue('myObservable', 0); - const myDerived = derived('myDerived', (reader) => { + const myDerived = derived(reader => { + /** @description myDerived */ const val = myObservable.read(reader); log.log(`myDerived.read(myObservable: ${val})`); return val; }); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun.run(myDerived: ${myDerived.read(reader)})`); }); assert.deepStrictEqual(log.getAndClearEntries(), [ @@ -616,13 +638,15 @@ suite('observables', () => { test('autorun reruns on indirect neutral observable double change when changes propagate', () => { const log = new Log(); const myObservable = observableValue('myObservable', 0); - const myDerived = derived('myDerived', (reader) => { + const myDerived = derived(reader => { + /** @description myDerived */ const val = myObservable.read(reader); log.log(`myDerived.read(myObservable: ${val})`); return val; }); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ log.log(`myAutorun.run(myDerived: ${myDerived.read(reader)})`); }); assert.deepStrictEqual(log.getAndClearEntries(), [ @@ -656,7 +680,8 @@ suite('observables', () => { const myObservable2 = new LoggingObservableValue('myObservable2', 0, log); const myObservable3 = new LoggingObservableValue('myObservable3', 0, log); - const d = autorun('autorun', (reader) => { + const d = autorun(reader => { + /** @description autorun */ if (observable1.read(reader) >= 2) { assert.deepStrictEqual(log.getAndClearEntries(), [ "myObservable1.set (value 2)", @@ -706,13 +731,15 @@ suite('observables', () => { const myObservable1 = new LoggingObservableValue('myObservable1', 0, log); const myObservable2 = new LoggingObservableValue('myObservable2', 0, log); - const myDerived1 = derived('myDerived1', (reader) => { + const myDerived1 = derived(reader => { + /** @description myDerived1 */ const val = myObservable1.read(reader); log.log(`myDerived1.read(myObservable: ${val})`); return val; }); - const myDerived2 = derived('myDerived2', (reader) => { + const myDerived2 = derived(reader => { + /** @description myDerived2 */ const val = myObservable2.read(reader); if (val === 1) { myDerived1.read(reader); @@ -721,7 +748,8 @@ suite('observables', () => { return val; }); - autorun('myAutorun', (reader) => { + autorun(reader => { + /** @description myAutorun */ const myDerived1Val = myDerived1.read(reader); const myDerived2Val = myDerived2.read(reader); log.log(`myAutorun.run(myDerived1: ${myDerived1Val}, myDerived2: ${myDerived2Val})`); @@ -739,7 +767,8 @@ suite('observables', () => { const log = new Log(); const myObservable = new LoggingObservableValue('myObservable', 0, log); - const myComputed = derived('myComputed', reader => { + const myComputed = derived(reader => { + /** @description myComputed */ let value = myObservable.read(reader); const origValue = value; log.log(`myComputed(myObservable: ${origValue}): start computing`); @@ -751,7 +780,8 @@ suite('observables', () => { return value; }); - autorun('myAutorun', reader => { + autorun(reader => { + /** @description myAutorun */ const value = myComputed.read(reader); log.log(`myAutorun(myComputed: ${value})`); }); @@ -785,7 +815,8 @@ suite('observables', () => { const log = new Log(); const myObservable = new LoggingObservableValue('myObservable', 0, log); - autorun('myAutorun', reader => { + autorun(reader => { + /** @description myAutorun */ const value = myObservable.read(reader); log.log(`myAutorun(myObservable: ${value}): start`); if (value !== 0 && value < 4) { @@ -825,19 +856,22 @@ suite('observables', () => { const log = new Log(); const myObservable = new LoggingObservableValue('myObservable', 0, log); - const myDerived1 = derived('myDerived1', reader => { + const myDerived1 = derived(reader => { + /** @description myDerived1 */ const value = myObservable.read(reader); log.log(`myDerived1(myObservable: ${value}): start computing`); return value; }); - const myDerived2 = derived('myDerived2', reader => { + const myDerived2 = derived(reader => { + /** @description myDerived2 */ const value = myDerived1.read(reader); log.log(`myDerived2(myDerived1: ${value}): start computing`); return value; }); - autorun('myAutorun', reader => { + autorun(reader => { + /** @description myAutorun */ const value = myDerived2.read(reader); log.log(`myAutorun(myDerived2: ${value})`); }); @@ -880,20 +914,23 @@ suite('observables', () => { const myObservable1 = new LoggingObservableValue('myObservable1', 0, log); const myObservable2 = new LoggingObservableValue('myObservable2', 0, log); - const myDerived2 = derived('myDerived2', reader => { + const myDerived2 = derived(reader => { + /** @description myDerived2 */ const val = myObservable2.read(reader); log.log(`myDerived2.computed(myObservable2: ${val})`); return val % 10; }); - const myDerived3 = derived('myDerived3', reader => { + const myDerived3 = derived(reader => { + /** @description myDerived3 */ const val1 = myObservable1.read(reader); const val2 = myDerived2.read(reader); log.log(`myDerived3.computed(myDerived1: ${val1}, myDerived2: ${val2})`); return `${val1} + ${val2}`; }); - autorun('myAutorun', reader => { + autorun(reader => { + /** @description myAutorun */ const val = myDerived3.read(reader); log.log(`myAutorun(myDerived3: ${val})`); }); @@ -931,15 +968,17 @@ suite('observables', () => { const myObservable1 = observableValue('myObservable1', 0); const myObservable2 = observableValue('myObservable2', 0); - const myDerived1 = derived('myDerived1', reader => { + const myDerived1 = derived(reader => { + /** @description myDerived1 */ return myObservable1.read(reader); }); - const myDerived2 = derived('myDerived2', reader => { + const myDerived2 = derived(reader => { + /** @description myDerived2 */ return myObservable2.read(reader); }); - const myDerivedA1 = derived('myDerivedA1', reader => { + const myDerivedA1 = derived(reader => /** @description myDerivedA1 */ { const d1 = myDerived1.read(reader); if (d1 === 1) { // This adds an observer while myDerived is still in update mode. @@ -949,11 +988,13 @@ suite('observables', () => { } }); - autorun('myAutorun1', reader => { + autorun(reader => { + /** @description myAutorun1 */ myDerivedA1.read(reader); }); - autorun('myAutorun2', reader => { + autorun(reader => { + /** @description myAutorun2 */ myDerived2.read(reader); }); @@ -962,6 +1003,60 @@ suite('observables', () => { myObservable2.set(1, tx); }); }); + + test('bug: fromObservableLight doesnt subscribe', () => { + const log = new Log(); + const myObservable = new LoggingObservableValue('myObservable', 0, log); + + const myDerived = derived(reader => /** @description myDerived */ { + const val = myObservable.read(reader); + log.log(`myDerived.computed(myObservable2: ${val})`); + return val % 10; + }); + + const e = Event.fromObservableLight(myDerived); + log.log('event created'); + e(() => { + log.log('event fired'); + }); + + myObservable.set(1, undefined); + + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'event created', + 'myObservable.firstObserverAdded', + 'myObservable.get', + 'myDerived.computed(myObservable2: 0)', + 'myObservable.set (value 1)', + 'myObservable.get', + 'myDerived.computed(myObservable2: 1)', + 'event fired', + ]); + }); + + test('dont run autorun after dispose', () => { + const log = new Log(); + const myObservable = new LoggingObservableValue('myObservable', 0, log); + + const d = autorun(reader => { + /** @description update */ + const v = myObservable.read(reader); + log.log('autorun, myObservable:' + v); + }); + + transaction(tx => { + myObservable.set(1, tx); + d.dispose(); + }); + + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'myObservable.firstObserverAdded', + 'myObservable.get', + 'autorun, myObservable:0', + 'myObservable.set (value 1)', + 'myObservable.lastObserverRemoved', + ]); + }); }); export class LoggingObserver implements IObserver { diff --git a/src/vs/base/test/common/prefixTree.test.ts b/src/vs/base/test/common/prefixTree.test.ts new file mode 100644 index 00000000000..b93921810d3 --- /dev/null +++ b/src/vs/base/test/common/prefixTree.test.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WellDefinedPrefixTree } from 'vs/base/common/prefixTree'; +import * as assert from 'assert'; + +suite('WellDefinedPrefixTree', () => { + let tree: WellDefinedPrefixTree; + + setup(() => { + tree = new WellDefinedPrefixTree(); + }); + + test('find', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'baz']; + tree.insert(key1, 42); + tree.insert(key2, 43); + assert.strictEqual(tree.find(key1), 42); + assert.strictEqual(tree.find(key2), 43); + assert.strictEqual(tree.find(['foo', 'baz', 'bop']), undefined); + assert.strictEqual(tree.find(['foo']), undefined); + }); + + test('hasParentOfKey', () => { + const key = ['foo', 'bar']; + tree.insert(key, 42); + + assert.strictEqual(tree.hasKeyOrParent(['foo', 'bar', 'baz']), true); + assert.strictEqual(tree.hasKeyOrParent(['foo', 'bar']), true); + assert.strictEqual(tree.hasKeyOrParent(['foo']), false); + assert.strictEqual(tree.hasKeyOrParent(['baz']), false); + }); + + + test('hasKeyOrChildren', () => { + const key = ['foo', 'bar']; + tree.insert(key, 42); + + assert.strictEqual(tree.hasKeyOrChildren([]), true); + assert.strictEqual(tree.hasKeyOrChildren(['foo']), true); + assert.strictEqual(tree.hasKeyOrChildren(['foo', 'bar']), true); + assert.strictEqual(tree.hasKeyOrChildren(['foo', 'bar', 'baz']), false); + }); + + test('hasKey', () => { + const key = ['foo', 'bar']; + tree.insert(key, 42); + + assert.strictEqual(tree.hasKey(key), true); + assert.strictEqual(tree.hasKey(['foo']), false); + assert.strictEqual(tree.hasKey(['baz']), false); + assert.strictEqual(tree.hasKey(['foo', 'bar', 'baz']), false); + }); + + test('size', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'baz']; + assert.strictEqual(tree.size, 0); + tree.insert(key1, 42); + assert.strictEqual(tree.size, 1); + tree.insert(key2, 43); + assert.strictEqual(tree.size, 2); + tree.insert(key2, 44); + assert.strictEqual(tree.size, 2); + }); + + test('mutate', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'baz']; + tree.insert(key1, 42); + tree.insert(key2, 43); + tree.mutate(key1, (value) => { + assert.strictEqual(value, 42); + return 44; + }); + assert.strictEqual(tree.find(key1), 44); + assert.strictEqual(tree.find(key2), 43); + }); + + test('delete', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'baz']; + tree.insert(key1, 42); + tree.insert(key2, 43); + assert.strictEqual(tree.size, 2); + + assert.strictEqual(tree.delete(key1), 42); + assert.strictEqual(tree.size, 1); + assert.strictEqual(tree.find(key1), undefined); + assert.strictEqual(tree.find(key2), 43); + + assert.strictEqual(tree.delete(key2), 43); + assert.strictEqual(tree.size, 0); + assert.strictEqual(tree.find(key1), undefined); + assert.strictEqual(tree.find(key2), undefined); + + tree.delete(key2); + assert.strictEqual(tree.size, 0); + }); + + test('delete child', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'bar', 'baz']; + tree.insert(key1, 42); + tree.insert(key2, 43); + assert.strictEqual(tree.size, 2); + + assert.strictEqual(tree.delete(key2), 43); + assert.strictEqual(tree.size, 1); + assert.strictEqual(tree.find(key1), 42); + assert.strictEqual(tree.find(key2), undefined); + }); + + test('delete noops if deleting parent', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'bar', 'baz']; + tree.insert(key2, 43); + assert.strictEqual(tree.size, 1); + + assert.strictEqual(tree.delete(key1), undefined); + assert.strictEqual(tree.size, 1); + assert.strictEqual(tree.find(key2), 43); + assert.strictEqual(tree.find(key1), undefined); + }); + + test('values', () => { + const key1 = ['foo', 'bar']; + const key2 = ['foo', 'baz']; + tree.insert(key1, 42); + tree.insert(key2, 43); + + assert.deepStrictEqual([...tree.values()], [43, 42]); + }); +}); diff --git a/src/vs/base/test/node/nodeStreams.test.ts b/src/vs/base/test/node/nodeStreams.test.ts new file mode 100644 index 00000000000..620f817dfc9 --- /dev/null +++ b/src/vs/base/test/node/nodeStreams.test.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { Writable } from 'stream'; +import * as assert from 'assert'; +import { StreamSplitter } from 'vs/base/node/nodeStreams'; + +suite('StreamSplitter', () => { + test('should split a stream on a single character splitter', (done) => { + const chunks: string[] = []; + const splitter = new StreamSplitter('\n'); + const writable = new Writable({ + write(chunk, _encoding, callback) { + chunks.push(chunk.toString()); + callback(); + }, + }); + + splitter.pipe(writable); + splitter.write('hello\nwor'); + splitter.write('ld\n'); + splitter.write('foo\nbar\nz'); + splitter.end(() => { + assert.deepStrictEqual(chunks, ['hello\n', 'world\n', 'foo\n', 'bar\n', 'z']); + done(); + }); + }); + + test('should split a stream on a multi-character splitter', (done) => { + const chunks: string[] = []; + const splitter = new StreamSplitter('---'); + const writable = new Writable({ + write(chunk, _encoding, callback) { + chunks.push(chunk.toString()); + callback(); + }, + }); + + splitter.pipe(writable); + splitter.write('hello---wor'); + splitter.write('ld---'); + splitter.write('foo---bar---z'); + splitter.end(() => { + assert.deepStrictEqual(chunks, ['hello---', 'world---', 'foo---', 'bar---', 'z']); + done(); + }); + }); +}); diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index cefdc23af5b..3a0fc605a71 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -173,7 +173,7 @@ flakySuite('PFS', function () { assert.ok(!fs.existsSync(testDir)); }); - test('copy, move and delete', async () => { + test('copy, rename and delete', async () => { const sourceDir = FileAccess.asFileUri('vs/base/test/node/pfs/fixtures').fsPath; const parentDir = join(tmpdir(), 'vsctests', 'pfs'); const targetDir = randomPath(parentDir); @@ -188,7 +188,7 @@ flakySuite('PFS', function () { assert.ok(fs.statSync(join(targetDir, 'examples')).isDirectory()); assert.ok(fs.existsSync(join(targetDir, 'examples', 'small.jxs'))); - await Promises.move(targetDir, targetDir2); + await Promises.rename(targetDir, targetDir2); assert.ok(!fs.existsSync(targetDir)); assert.ok(fs.existsSync(targetDir2)); @@ -198,7 +198,34 @@ flakySuite('PFS', function () { assert.ok(fs.statSync(join(targetDir2, 'examples')).isDirectory()); assert.ok(fs.existsSync(join(targetDir2, 'examples', 'small.jxs'))); - await Promises.move(join(targetDir2, 'index.html'), join(targetDir2, 'index_moved.html')); + await Promises.rename(join(targetDir2, 'index.html'), join(targetDir2, 'index_moved.html')); + + assert.ok(!fs.existsSync(join(targetDir2, 'index.html'))); + assert.ok(fs.existsSync(join(targetDir2, 'index_moved.html'))); + + await Promises.rm(parentDir); + + assert.ok(!fs.existsSync(parentDir)); + }); + + test('rename without retry', async () => { + const sourceDir = FileAccess.asFileUri('vs/base/test/node/pfs/fixtures').fsPath; + const parentDir = join(tmpdir(), 'vsctests', 'pfs'); + const targetDir = randomPath(parentDir); + const targetDir2 = randomPath(parentDir); + + await Promises.copy(sourceDir, targetDir, { preserveSymlinks: true }); + await Promises.rename(targetDir, targetDir2, false); + + assert.ok(!fs.existsSync(targetDir)); + assert.ok(fs.existsSync(targetDir2)); + assert.ok(fs.existsSync(join(targetDir2, 'index.html'))); + assert.ok(fs.existsSync(join(targetDir2, 'site.css'))); + assert.ok(fs.existsSync(join(targetDir2, 'examples'))); + assert.ok(fs.statSync(join(targetDir2, 'examples')).isDirectory()); + assert.ok(fs.existsSync(join(targetDir2, 'examples', 'small.jxs'))); + + await Promises.rename(join(targetDir2, 'index.html'), join(targetDir2, 'index_moved.html'), false); assert.ok(!fs.existsSync(join(targetDir2, 'index.html'))); assert.ok(fs.existsSync(join(targetDir2, 'index_moved.html'))); diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 65df427d79e..ec18c5ca0f4 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -54,7 +54,7 @@ try { const func = ( trustedTypesPolicy - ? globalThis.eval(trustedTypesPolicy.createScript('', 'true')) + ? globalThis.eval(trustedTypesPolicy.createScript('', 'true')) // CodeQL [SM01632] fetch + eval is used on the web worker instead of importScripts if possible because importScripts is synchronous and we observed deadlocks on Safari : new Function('true') // CodeQL [SM01632] fetch + eval is used on the web worker instead of importScripts if possible because importScripts is synchronous and we observed deadlocks on Safari ); func.call(globalThis); diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index a41b5dbf7bd..addc28f0c4d 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -43,7 +43,9 @@ // Set up nls if the user is not using the default language (English) const nlsConfig = {}; - const locale = window.localStorage.getItem('vscode.nls.locale') || navigator.language; + // Normalize locale to lowercase because translationServiceUrl is case-sensitive. + // ref: https://github.com/microsoft/vscode/issues/187795 + const locale = window.localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase(); if (!locale.startsWith('en')) { nlsConfig['vs/nls'] = { availableLanguages: { diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 62a0406d7f5..3ceea02d892 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -4,38 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { isStandalone } from 'vs/base/browser/browser'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { parse } from 'vs/base/common/marshalling'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { request } from 'vs/base/parts/request/browser/request'; import product from 'vs/platform/product/common/product'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window'; import { create } from 'vs/workbench/workbench.web.main'; import { posix } from 'vs/base/common/path'; import { ltrim } from 'vs/base/common/strings'; -import type { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api'; import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; +import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; +import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; -interface ICredential { - service: string; - account: string; - password: string; -} +class LocalStorageSecretStorageProvider implements ISecretStorageProvider { + private static readonly STORAGE_KEY = 'secrets.provider'; -class LocalStorageCredentialsProvider implements ICredentialsProvider { + private _secrets: Record | undefined; - private static readonly CREDENTIALS_STORAGE_KEY = 'credentials.provider'; - - private readonly authService: string | undefined; + type: 'in-memory' | 'persisted' | 'unknown' = 'persisted'; constructor() { - let authSessionInfo: { readonly id: string; readonly accessToken: string; readonly providerId: string; readonly canSignOut?: boolean; readonly scopes: string[][] } | undefined; + let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined; const authSessionElement = document.getElementById('vscode-workbench-auth-session'); const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; if (authSessionElementAttribute) { @@ -46,11 +40,15 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { if (authSessionInfo) { // Settings Sync Entry - this.setPassword(`${product.urlProtocol}.login`, 'account', JSON.stringify(authSessionInfo)); + this.set(`${product.urlProtocol}.loginAccount`, JSON.stringify(authSessionInfo)); // Auth extension Entry - this.authService = `${product.urlProtocol}-${authSessionInfo.providerId}.login`; - this.setPassword(this.authService, 'account', JSON.stringify(authSessionInfo.scopes.map(scopes => ({ + if (authSessionInfo.providerId !== 'github') { + console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`); + return; + } + const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' }); + this.set(authAccount, JSON.stringify(authSessionInfo.scopes.map(scopes => ({ id: authSessionInfo!.id, scopes, accessToken: authSessionInfo!.accessToken @@ -58,121 +56,44 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { } } - private _credentials: ICredential[] | undefined; - private get credentials(): ICredential[] { - if (!this._credentials) { + get(key: string): Promise { + return Promise.resolve(this.secrets[key]); + } + set(key: string, value: string): Promise { + this.secrets[key] = value; + this.save(); + + return Promise.resolve(); + } + async delete(key: string): Promise { + delete this.secrets[key]; + + this.save(); + + return Promise.resolve(); + } + + private get secrets(): Record { + if (!this._secrets) { try { - const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY); + const serializedCredentials = window.localStorage.getItem(LocalStorageSecretStorageProvider.STORAGE_KEY); if (serializedCredentials) { - this._credentials = JSON.parse(serializedCredentials); + this._secrets = JSON.parse(serializedCredentials); } } catch (error) { // ignore } - if (!Array.isArray(this._credentials)) { - this._credentials = []; + if (!(this._secrets instanceof Object)) { + this._secrets = {}; } } - return this._credentials; + return this._secrets; } private save(): void { - window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY, JSON.stringify(this.credentials)); - } - - async getPassword(service: string, account: string): Promise { - return this.doGetPassword(service, account); - } - - private async doGetPassword(service: string, account?: string): Promise { - for (const credential of this.credentials) { - if (credential.service === service) { - if (typeof account !== 'string' || account === credential.account) { - return credential.password; - } - } - } - - return null; - } - - async setPassword(service: string, account: string, password: string): Promise { - this.doDeletePassword(service, account); - - this.credentials.push({ service, account, password }); - - this.save(); - - try { - if (password && service === this.authService) { - const value = JSON.parse(password); - if (Array.isArray(value) && value.length === 0) { - await this.logout(service); - } - } - } catch (error) { - console.log(error); - } - } - - async deletePassword(service: string, account: string): Promise { - const result = await this.doDeletePassword(service, account); - - if (result && service === this.authService) { - try { - await this.logout(service); - } catch (error) { - console.log(error); - } - } - - return result; - } - - private async doDeletePassword(service: string, account: string): Promise { - let found = false; - - this._credentials = this.credentials.filter(credential => { - if (credential.service === service && credential.account === account) { - found = true; - - return false; - } - - return true; - }); - - if (found) { - this.save(); - } - - return found; - } - - async findPassword(service: string): Promise { - return this.doGetPassword(service); - } - - async findCredentials(service: string): Promise> { - return this.credentials - .filter(credential => credential.service === service) - .map(({ account, password }) => ({ account, password })); - } - - private async logout(service: string): Promise { - const queryValues: Map = new Map(); - queryValues.set('logout', String(true)); - queryValues.set('service', service); - - await request({ - url: doCreateUri('/auth/logout', queryValues).toString(true) - }, CancellationToken.None); - } - - async clear(): Promise { - window.localStorage.removeItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY); + window.localStorage.setItem(LocalStorageSecretStorageProvider.STORAGE_KEY, JSON.stringify(this.secrets)); } } @@ -469,24 +390,6 @@ class WorkspaceProvider implements IWorkspaceProvider { } } -function doCreateUri(path: string, queryValues: Map): URI { - let query: string | undefined = undefined; - - if (queryValues) { - let index = 0; - queryValues.forEach((value, key) => { - if (!query) { - query = ''; - } - - const prefix = (index++ === 0) ? '' : '&'; - query += `${prefix}${key}=${encodeURIComponent(value)}`; - }); - } - - return URI.parse(window.location.href).with({ path, query }); -} - (function () { // Find config by checking for DOM @@ -504,6 +407,6 @@ function doCreateUri(path: string, queryValues: Map): URI { settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, workspaceProvider: WorkspaceProvider.create(config), urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), - credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider() + secretStorageProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local secret storage provider */ : new LocalStorageSecretStorageProvider() }); })(); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index b907a0693ff..bd6fb95a75d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -9,7 +9,7 @@ import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { hostname, release } from 'os'; import { VSBuffer } from 'vs/base/common/buffer'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { isEqualOrParent } from 'vs/base/common/extpath'; import { once } from 'vs/base/common/functional'; import { stripComments } from 'vs/base/common/json'; @@ -336,7 +336,11 @@ export class CodeApplication extends Disposable { // We handle uncaught exceptions here to prevent electron from opening a dialog to the user setUnexpectedErrorHandler(error => this.onUnexpectedError(error)); - process.on('uncaughtException', error => onUnexpectedError(error)); + process.on('uncaughtException', error => { + if (!isSigPipeError(error)) { + onUnexpectedError(error); + } + }); process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason)); // Dispose on shutdown @@ -930,7 +934,7 @@ export class CodeApplication extends Disposable { services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [this.userEnv])); // Encryption - services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService, [machineId])); + services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService)); // Keyboard Layout services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService)); diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index 9e56bcfa902..6a18eba4eb5 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -8,11 +8,14 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; +import { withNullAsUndefined } from 'vs/base/common/types'; import { generateUuid } from 'vs/base/common/uuid'; import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; +import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IApplicationStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; interface ElectronAuthenticationResponseDetails extends AuthenticationResponseDetails { @@ -56,7 +59,8 @@ enum ProxyAuthState { export class ProxyAuthHandler extends Disposable { - private readonly PROXY_CREDENTIALS_SERVICE_KEY = `${this.productService.urlProtocol}.proxy-credentials`; + private readonly OLD_PROXY_CREDENTIALS_SERVICE_KEY = `${this.productService.urlProtocol}.proxy-credentials`; + private readonly PROXY_CREDENTIALS_SERVICE_KEY = 'proxy-credentials://'; private pendingProxyResolve: Promise | undefined = undefined; @@ -69,6 +73,7 @@ export class ProxyAuthHandler extends Disposable { @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @ICredentialsMainService private readonly credentialsService: ICredentialsMainService, @IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService, + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, @IProductService private readonly productService: IProductService ) { super(); @@ -141,6 +146,34 @@ export class ProxyAuthHandler extends Disposable { return undefined; } + // TODO: remove this migration in a release or two. + private async getAndMigrateProxyCredentials(authInfoHash: string): Promise<{ storedUsername: string | undefined; storedPassword: string | undefined }> { + // Find any previously stored credentials + try { + let encryptedSerializedProxyCredentials = this.applicationStorageMainService.get(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION); + let decryptedSerializedProxyCredentials: string | undefined; + if (!encryptedSerializedProxyCredentials) { + encryptedSerializedProxyCredentials = withNullAsUndefined(await this.credentialsService.getPassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash)); + if (encryptedSerializedProxyCredentials) { + // re-encrypt to force new encryption algorithm to apply + decryptedSerializedProxyCredentials = await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials); + encryptedSerializedProxyCredentials = await this.encryptionMainService.encrypt(decryptedSerializedProxyCredentials); + this.applicationStorageMainService.store(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, encryptedSerializedProxyCredentials, StorageScope.APPLICATION, StorageTarget.MACHINE); + // Remove it from the old location since it's in the new location. + await this.credentialsService.deletePassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); + } + } + if (encryptedSerializedProxyCredentials) { + const credentials: Credentials = JSON.parse(decryptedSerializedProxyCredentials ?? await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials)); + + return { storedUsername: credentials.username, storedPassword: credentials.password }; + } + } catch (error) { + this.logService.error(error); // handle errors by asking user for login via dialog + } + return { storedUsername: undefined, storedPassword: undefined }; + } + private async doResolveProxyCredentials(authInfo: AuthInfo): Promise { this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo); @@ -149,21 +182,7 @@ export class ProxyAuthHandler extends Disposable { // given the properties of the auth request // (see https://github.com/microsoft/vscode/issues/109497) const authInfoHash = String(hash({ scheme: authInfo.scheme, host: authInfo.host, port: authInfo.port })); - - // Find any previously stored credentials - let storedUsername: string | undefined = undefined; - let storedPassword: string | undefined = undefined; - try { - const encryptedSerializedProxyCredentials = await this.credentialsService.getPassword(this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); - if (encryptedSerializedProxyCredentials) { - const credentials: Credentials = JSON.parse(await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials)); - - storedUsername = credentials.username; - storedPassword = credentials.password; - } - } catch (error) { - this.logService.error(error); // handle errors by asking user for login via dialog - } + const { storedUsername, storedPassword } = await this.getAndMigrateProxyCredentials(authInfoHash); // Reply with stored credentials unless we used them already. // In that case we need to show a login dialog again because @@ -212,9 +231,9 @@ export class ProxyAuthHandler extends Disposable { try { if (reply.remember) { const encryptedSerializedCredentials = await this.encryptionMainService.encrypt(JSON.stringify(credentials)); - await this.credentialsService.setPassword(this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash, encryptedSerializedCredentials); + this.applicationStorageMainService.store(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, encryptedSerializedCredentials, StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - await this.credentialsService.deletePassword(this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); + this.applicationStorageMainService.remove(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION); } } catch (error) { this.logService.error(error); // handle gracefully diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 6e960fc47c7..0174a24c3c8 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -70,6 +70,7 @@ import { ILoggerMainService, LoggerMainService } from 'vs/platform/log/electron- import { LogService } from 'vs/platform/log/common/logService'; import { massageMessageBoxOptions } from 'vs/platform/dialogs/common/dialogs'; import { SaveStrategy, StateService } from 'vs/platform/state/node/stateService'; +import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; /** * The main VS Code entry point. @@ -178,6 +179,10 @@ class CodeMain { const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // Use FileUserDataProvider for user data to + // enable atomic read / write operations. + fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, logService)); + // URI Identity const uriIdentityService = new UriIdentityService(fileService); services.set(IUriIdentityService, uriIdentityService); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 73268f714f7..a003267c043 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -62,6 +62,7 @@ import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement import { LogService } from 'vs/platform/log/common/logService'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { localize } from 'vs/nls'; +import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; class CliMain extends Disposable { @@ -145,6 +146,10 @@ class CliMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // Use FileUserDataProvider for user data to + // enable atomic read / write operations. + fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, logService)); + // Uri Identity const uriIdentityService = new UriIdentityService(fileService); services.set(IUriIdentityService, uriIdentityService); @@ -187,7 +192,8 @@ class CliMain extends Disposable { services.set(IUriIdentityService, new UriIdentityService(fileService)); // Request - services.set(IRequestService, new SyncDescriptor(RequestService, undefined, true)); + const requestService = new RequestService(configurationService, environmentService, logService, loggerService); + services.set(IRequestService, requestService); // Download Service services.set(IDownloadService, new SyncDescriptor(DownloadService, undefined, true)); @@ -207,7 +213,7 @@ class CliMain extends Disposable { const isInternal = isInternalTelemetry(productService, configurationService); if (supportsTelemetry(productService, environmentService)) { if (productService.aiConfig && productService.aiConfig.ariaKey) { - appenders.push(new OneDataSystemAppender(isInternal, 'monacoworkbench', null, productService.aiConfig.ariaKey)); + appenders.push(new OneDataSystemAppender(requestService, isInternal, 'monacoworkbench', null, productService.aiConfig.ariaKey)); } const config: ITelemetryServiceConfig = { diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 28053c1cdd6..f911b0da431 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -26,7 +26,6 @@ import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsServ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { SharedProcessEnvironmentService } from 'vs/platform/sharedProcess/node/sharedProcessEnvironmentService'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -112,6 +111,7 @@ import { RemoteStorageService } from 'vs/platform/storage/common/storageService' import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; class SharedProcessMain extends Disposable { @@ -191,7 +191,7 @@ class SharedProcessMain extends Disposable { services.set(IPolicyService, policyService); // Environment - const environmentService = new SharedProcessEnvironmentService(this.configuration.args, productService); + const environmentService = new NativeEnvironmentService(this.configuration.args, productService); services.set(INativeEnvironmentService, environmentService); // Logger @@ -251,7 +251,8 @@ class SharedProcessMain extends Disposable { services.set(IUriIdentityService, uriIdentityService); // Request - services.set(IRequestService, new RequestChannelClient(mainProcessService.getChannel('request'))); + const requestService = new RequestChannelClient(mainProcessService.getChannel('request')); + services.set(IRequestService, requestService); // Checksum services.set(IChecksumService, new SyncDescriptor(ChecksumService, undefined, false /* proxied to other processes */)); @@ -279,7 +280,7 @@ class SharedProcessMain extends Disposable { const logAppender = new TelemetryLogAppender(logService, loggerService, environmentService, productService); appenders.push(logAppender); if (productService.aiConfig?.ariaKey) { - const collectorAppender = new OneDataSystemAppender(internalTelemetry, 'monacoworkbench', null, productService.aiConfig.ariaKey); + const collectorAppender = new OneDataSystemAppender(requestService, internalTelemetry, 'monacoworkbench', null, productService.aiConfig.ariaKey); this._register(toDisposable(() => collectorAppender.flush())); // Ensure the 1DS appender is disposed so that it flushes remaining data appenders.push(collectorAppender); } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 0d5c3317051..94fa1a8bdb1 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -35,6 +35,7 @@ import { TokenizationRegistry } from 'vs/editor/common/languages'; import { ColorId, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes'; import { Color } from 'vs/base/common/color'; import { IME } from 'vs/base/common/ime'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export interface IVisibleRangeProvider { visibleRangeForPosition(position: Position): HorizontalPosition | null; @@ -140,7 +141,12 @@ export class TextAreaHandler extends ViewPart { public readonly textAreaCover: FastDomNode; private readonly _textAreaInput: TextAreaInput; - constructor(context: ViewContext, viewController: ViewController, visibleRangeProvider: IVisibleRangeProvider) { + constructor( + context: ViewContext, + viewController: ViewController, + visibleRangeProvider: IVisibleRangeProvider, + @IKeybindingService private readonly _keybindingService: IKeybindingService + ) { super(context); this._viewController = viewController; @@ -177,6 +183,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setAttribute('autocomplete', 'off'); this.textArea.setAttribute('spellcheck', 'false'); this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); + this.textArea.setAttribute('aria-required', options.get(EditorOption.ariaRequired) ? 'true' : 'false'); this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex))); this.textArea.setAttribute('role', 'textbox'); this.textArea.setAttribute('aria-roledescription', nls.localize('editor', "editor")); @@ -552,7 +559,21 @@ export class TextAreaHandler extends ViewPart { private _getAriaLabel(options: IComputedEditorOptions): string { const accessibilitySupport = options.get(EditorOption.accessibilitySupport); if (accessibilitySupport === AccessibilitySupport.Disabled) { - return nls.localize('accessibilityOffAriaLabel', "The editor is not accessible at this time. Press {0} for options.", platform.isLinux ? 'Shift+Alt+F1' : 'Alt+F1'); + + const toggleKeybindingLabel = this._keybindingService.lookupKeybinding('editor.action.toggleScreenReaderAccessibilityMode')?.getAriaLabel(); + const runCommandKeybindingLabel = this._keybindingService.lookupKeybinding('workbench.action.showCommands')?.getAriaLabel(); + const keybindingEditorKeybindingLabel = this._keybindingService.lookupKeybinding('workbench.action.openGlobalKeybindings')?.getAriaLabel(); + const editorNotAccessibleMessage = nls.localize('accessibilityModeOff', "The editor is not accessible at this time."); + if (toggleKeybindingLabel) { + return nls.localize('accessibilityOffAriaLabel', "{0} To enable screen reader optimized mode, use {1}", editorNotAccessibleMessage, toggleKeybindingLabel); + } else if (runCommandKeybindingLabel) { + return nls.localize('accessibilityOffAriaLabelNoKb', "{0} To enable screen reader optimized mode, open the quick pick with {1} and run the command Toggle Screen Reader Accessibility Mode, which is currently not triggerable via keyboard.", editorNotAccessibleMessage, runCommandKeybindingLabel); + } else if (keybindingEditorKeybindingLabel) { + return nls.localize('accessibilityOffAriaLabelNoKbs', "{0} Please assign a keybinding for the command Toggle Screen Reader Accessibility Mode by accessing the keybindings editor with {1} and run it.", editorNotAccessibleMessage, keybindingEditorKeybindingLabel); + } else { + // SOS + return editorNotAccessibleMessage; + } } return options.get(EditorOption.ariaLabel); } @@ -601,6 +622,7 @@ export class TextAreaHandler extends ViewPart { const { tabSize } = this._context.viewModel.model.getOptions(); this.textArea.domNode.style.tabSize = `${tabSize * this._fontInfo.spaceWidth}px`; this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); + this.textArea.setAttribute('aria-required', options.get(EditorOption.ariaRequired) ? 'true' : 'false'); this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex))); if (e.hasChanged(EditorOption.domReadOnly) || e.hasChanged(EditorOption.readOnly)) { diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 69c0dfd4ad7..36da11ef183 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -650,9 +650,9 @@ export class TextAreaInput extends Disposable { } } -class ClipboardEventUtils { +export const ClipboardEventUtils = { - public static getTextData(clipboardData: DataTransfer): [string, ClipboardStoredMetadata | null] { + getTextData(clipboardData: DataTransfer): [string, ClipboardStoredMetadata | null] { const text = clipboardData.getData(Mimes.text); let metadata: ClipboardStoredMetadata | null = null; const rawmetadata = clipboardData.getData('vscode-editor-data'); @@ -674,16 +674,16 @@ class ClipboardEventUtils { } return [text, metadata]; - } + }, - public static setTextData(clipboardData: DataTransfer, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void { + setTextData(clipboardData: DataTransfer, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void { clipboardData.setData(Mimes.text, text); if (typeof html === 'string') { clipboardData.setData('text/html', html); } clipboardData.setData('vscode-editor-data', JSON.stringify(metadata)); } -} +}; export class TextAreaWrapper extends Disposable implements ICompleteTextAreaWrapper { diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 2cd8aabfa0d..d037337887c 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -1086,6 +1086,12 @@ export interface ICodeEditor extends editorCommon.IEditor { hasModel(): this is IActiveCodeEditor; setBanner(bannerDomNode: HTMLElement | null, height: number): void; + + /** + * Is called when the model has been set, view state was restored and options are updated. + * This is the best place to compute data for the viewport (such as tokens). + */ + handleInitialized?(): void; } /** @@ -1257,9 +1263,9 @@ export interface IDiffEditor extends editorCommon.IEditor { */ revealFirstDiff(): unknown; - diffReviewNext(): void; + accessibleDiffViewerNext(): void; - diffReviewPrev(): void; + accessibleDiffViewerPrev(): void; } /** diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index 82d1fb7a269..0e791b14305 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -54,6 +54,7 @@ import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { WhitespaceOverlay } from 'vs/editor/browser/viewParts/whitespace/whitespace'; import { GlyphMarginWidgets } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; import { GlyphMarginLane } from 'vs/editor/common/model'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export interface IContentWidgetData { @@ -106,7 +107,8 @@ export class View extends ViewEventHandler { colorTheme: IColorTheme, model: IViewModel, userInputEvents: ViewUserInputEvents, - overflowWidgetsDomNode: HTMLElement | undefined + overflowWidgetsDomNode: HTMLElement | undefined, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); this._selections = [new Selection(1, 1, 1, 1)]; @@ -123,7 +125,7 @@ export class View extends ViewEventHandler { this._viewParts = []; // Keyboard handler - this._textAreaHandler = new TextAreaHandler(this._context, viewController, this._createTextAreaHandlerHelper()); + this._textAreaHandler = this._instantiationService.createInstance(TextAreaHandler, this._context, viewController, this._createTextAreaHandlerHelper()); this._viewParts.push(this._textAreaHandler); // These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.) diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css index af5c8a71f23..7d1a4475960 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css @@ -16,4 +16,17 @@ position: absolute; display: flex; align-items: center; + justify-content: center; +} + +/* + Ensure spinning icons are pixel-perfectly centered and avoid wobble. + This is only applied to icons that spin to avoid unnecessary + GPU layers and blurry subpixel AA. +*/ +.monaco-editor .glyph-margin-widgets .cgmr.codicon-modifier-spin::before { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css index fa78807bf71..ed132669757 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css @@ -7,11 +7,3 @@ position: absolute; box-sizing: border-box; } - -.monaco-editor .lines-content .core-guide-indent { - box-shadow: 1px 0 0 0 var(--vscode-editorIndentGuide-background) inset; -} - -.monaco-editor .lines-content .core-guide-indent-active { - box-shadow: 1px 0 0 0 var(--vscode-editorIndentGuide-activeBackground, --vscode-editorIndentGuide-background) inset; -} diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 43caef8a5cd..a93cf75a530 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -5,7 +5,7 @@ import 'vs/css!./indentGuides'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6 } from 'vs/editor/common/core/editorColorRegistry'; +import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6, editorIndentGuide1, editorIndentGuide2, editorIndentGuide3, editorIndentGuide4, editorIndentGuide5, editorIndentGuide6, editorActiveIndentGuide1, editorActiveIndentGuide2, editorActiveIndentGuide3, editorActiveIndentGuide4, editorActiveIndentGuide5, editorActiveIndentGuide6 } from 'vs/editor/common/core/editorColorRegistry'; import { RenderingContext } from 'vs/editor/browser/view/renderingContext'; import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; import * as viewEvents from 'vs/editor/common/viewEvents'; @@ -224,7 +224,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { new IndentGuide( indentGuide, -1, - isActive ? 'core-guide-indent-active' : 'core-guide-indent', + `core-guide-indent lvl-${(indentLvl - 1) % 30}` + (isActive ? ' indent-active' : ''), null, -1, -1, @@ -270,6 +270,14 @@ registerThemingParticipant((theme, collector) => { ]; const colorProvider = new BracketPairGuidesClassNames(); + const indentColors = [ + { indentColor: editorIndentGuide1, indentColorActive: editorActiveIndentGuide1 }, + { indentColor: editorIndentGuide2, indentColorActive: editorActiveIndentGuide2 }, + { indentColor: editorIndentGuide3, indentColorActive: editorActiveIndentGuide3 }, + { indentColor: editorIndentGuide4, indentColorActive: editorActiveIndentGuide4 }, + { indentColor: editorIndentGuide5, indentColorActive: editorActiveIndentGuide5 }, + { indentColor: editorIndentGuide6, indentColorActive: editorActiveIndentGuide6 }, + ]; const colorValues = colors .map(c => { @@ -291,6 +299,25 @@ registerThemingParticipant((theme, collector) => { }) .filter(isDefined); + const indentColorValues = indentColors + .map(c => { + const indentColor = theme.getColor(c.indentColor); + const indentColorActive = theme.getColor(c.indentColorActive); + + const effectiveIndentColor = transparentToUndefined(indentColor); + const effectiveIndentColorActive = transparentToUndefined(indentColorActive); + + if (!effectiveIndentColor || !effectiveIndentColorActive) { + return undefined; + } + + return { + indentColor: effectiveIndentColor, + indentColorActive: effectiveIndentColorActive, + }; + }) + .filter(isDefined); + if (colorValues.length > 0) { for (let level = 0; level < 30; level++) { const colors = colorValues[level % colorValues.length]; @@ -305,4 +332,14 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .horizontal-top.${colorProvider.activeClassName} { border-top: 1px solid var(--guide-color-active); }`); collector.addRule(`.monaco-editor .horizontal-bottom.${colorProvider.activeClassName} { border-bottom: 1px solid var(--guide-color-active); }`); } + + if (indentColorValues.length > 0) { + for (let level = 0; level < 30; level++) { + const colors = indentColorValues[level % indentColorValues.length]; + collector.addRule(`.monaco-editor .lines-content .core-guide-indent.lvl-${level} { --indent-color: ${colors.indentColor}; --indent-color-active: ${colors.indentColorActive}; }`); + } + + collector.addRule(`.monaco-editor .lines-content .core-guide-indent { box-shadow: 1px 0 0 0 var(--indent-color) inset; }`); + collector.addRule(`.monaco-editor .lines-content .core-guide-indent.indent-active { box-shadow: 1px 0 0 0 var(--indent-color-active) inset; }`); + } }); diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 031f8a8f612..e5c416a9e4b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -377,7 +377,7 @@ class MinimapLayout { const partialLine = (scrollTop - viewportStartLineNumberVerticalOffset) / lineHeight; let sliderTopAligned: number; - if (scrollTop > options.paddingTop) { + if (scrollTop >= options.paddingTop) { sliderTopAligned = (viewportStartLineNumber - startLineNumber + topPaddingLineCount + partialLine) * minimapLineHeight / pixelRatio; } else { sliderTopAligned = (scrollTop / options.paddingTop) * (topPaddingLineCount + partialLine) * minimapLineHeight / pixelRatio; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 59764b9ea97..f6389bf5410 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -42,7 +42,7 @@ import { editorErrorForeground, editorHintForeground, editorInfoForeground, edit import { VerticalRevealType } from 'vs/editor/common/viewEvents'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyValue, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -73,6 +73,8 @@ export interface ICodeEditorWidgetOptions { /** * Contributions to instantiate. + * When provided, only the contributions included will be instantiated. + * To include the defaults, those must be provided as well via [...EditorExtensionsRegistry.getEditorContributions()] * Defaults to EditorExtensionsRegistry.getEditorContributions(). */ contributions?: IEditorContributionDescription[]; @@ -1021,6 +1023,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } } + public handleInitialized(): void { + this._getViewModel()?.visibleLinesStabilized(); + } + public onVisible(): void { this._modelData?.view.refreshFocusState(); } @@ -1308,7 +1314,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE for (const decorationOption of decorationOptions) { let typeKey = decorationTypeKey; if (decorationOption.renderOptions) { - // identify custom reder options by a hash code over all keys and values + // identify custom render options by a hash code over all keys and values // For custom render options register a decoration type if necessary const subType = hash(decorationOption.renderOptions).toString(16); // The fact that `decorationTypeKey` appears in the typeKey has no influence @@ -1847,7 +1853,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._themeService.getColorTheme(), viewModel, viewUserInputEvents, - this._overflowWidgetsDomNode + this._overflowWidgetsDomNode, + this._instantiationService ); return [view, true]; @@ -1911,6 +1918,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private removeDropIndicator(): void { this._dropIntoEditorDecorations.clear(); } + + public setContextValue(key: string, value: ContextKeyValue): void { + this._contextKeyService.createKey(key, value); + } } const enum BooleanEventValue { diff --git a/src/vs/editor/browser/widget/diffEditor.contribution.ts b/src/vs/editor/browser/widget/diffEditor.contribution.ts index 665d845f164..9506f2bd5a7 100644 --- a/src/vs/editor/browser/widget/diffEditor.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditor.contribution.ts @@ -4,56 +4,82 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -class DiffReviewNext extends EditorAction { +const accessibleDiffViewerCategory: ILocalizedString = { + value: localize('accessibleDiffViewer', 'Accessible Diff Viewer'), + original: 'Accessible Diff Viewer', +}; + +export class AccessibleDiffViewerNext extends Action2 { + public static id = 'editor.action.accessibleDiffViewer.next'; + constructor() { super({ - id: 'editor.action.diffReview.next', - label: localize('editor.action.diffReview.next', "Go to Next Difference"), - alias: 'Go to Next Difference', + id: AccessibleDiffViewerNext.id, + title: { value: localize('editor.action.accessibleDiffViewer.next', "Go to Next Difference"), original: 'Go to Next Difference' }, + category: accessibleDiffViewerCategory, precondition: ContextKeyExpr.has('isInDiffEditor'), - kbOpts: { - kbExpr: null, + keybinding: { primary: KeyCode.F7, weight: KeybindingWeight.EditorContrib - } + }, + f1: true, }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public override run(accessor: ServicesAccessor): void { const diffEditor = findFocusedDiffEditor(accessor); - diffEditor?.diffReviewNext(); + diffEditor?.accessibleDiffViewerNext(); } } -class DiffReviewPrev extends EditorAction { +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: AccessibleDiffViewerNext.id, + title: localize('Open Accessible Diff Viewer', "Open Accessible Diff Viewer"), + }, + order: 10, + group: '2_diff', + when: ContextKeyExpr.and( + EditorContextKeys.accessibleDiffViewerVisible.negate(), + ContextKeyExpr.has('isInDiffEditor'), + ), +}); + +export class AccessibleDiffViewerPrev extends Action2 { + public static id = 'editor.action.accessibleDiffViewer.prev'; + constructor() { super({ - id: 'editor.action.diffReview.prev', - label: localize('editor.action.diffReview.prev', "Go to Previous Difference"), - alias: 'Go to Previous Difference', + id: AccessibleDiffViewerPrev.id, + title: { value: localize('editor.action.accessibleDiffViewer.prev', "Go to Previous Difference"), original: 'Go to Previous Difference' }, + category: accessibleDiffViewerCategory, precondition: ContextKeyExpr.has('isInDiffEditor'), - kbOpts: { - kbExpr: null, + keybinding: { primary: KeyMod.Shift | KeyCode.F7, weight: KeybindingWeight.EditorContrib - } + }, + f1: true, }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public override run(accessor: ServicesAccessor): void { const diffEditor = findFocusedDiffEditor(accessor); - diffEditor?.diffReviewPrev(); + diffEditor?.accessibleDiffViewerPrev(); } } -function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { +export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { const codeEditorService = accessor.get(ICodeEditorService); const diffEditors = codeEditorService.listDiffEditors(); const activeCodeEditor = codeEditorService.getFocusedCodeEditor() ?? codeEditorService.getActiveCodeEditor(); @@ -67,8 +93,32 @@ function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { return diffEditor; } } + + if (document.activeElement) { + for (const d of diffEditors) { + const container = d.getContainerDomNode(); + if (isElementOrParentOf(container, document.activeElement)) { + return d; + } + } + } + return null; } -registerEditorAction(DiffReviewNext); -registerEditorAction(DiffReviewPrev); +function isElementOrParentOf(elementOrParent: Element, element: Element): boolean { + let e: Element | null = element; + while (e) { + if (e === elementOrParent) { + return true; + } + e = e.parentElement; + } + return false; +} + +CommandsRegistry.registerCommandAlias('editor.action.diffReview.next', AccessibleDiffViewerNext.id); +registerAction2(AccessibleDiffViewerNext); + +CommandsRegistry.registerCommandAlias('editor.action.diffReview.prev', AccessibleDiffViewerPrev.id); +registerAction2(AccessibleDiffViewerPrev); diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index e21c1e9f3be..4eae29a335e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -299,6 +299,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE collapseUnchangedRegions: false, }, isInEmbeddedEditor: false, + onlyShowAccessibleDiffViewer: false, + renderSideBySideInlineBreakpoint: 0, + useInlineViewWhenSpaceIsLimited: false, }); this.isEmbeddedDiffEditorKey = EditorContextKeys.isEmbeddedDiffEditor.bindTo(this._contextKeyService); @@ -444,11 +447,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return dom.isAncestor(document.activeElement, this._domElement); } - public diffReviewNext(): void { + public accessibleDiffViewerNext(): void { this._reviewPane.next(); } - public diffReviewPrev(): void { + public accessibleDiffViewerPrev(): void { this._reviewPane.prev(); } @@ -1368,7 +1371,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._originalDomNode.style.width = splitPoint + 'px'; this._originalDomNode.style.left = '0px'; - this._modifiedDomNode.style.width = (width - splitPoint) + 'px'; + this._modifiedDomNode.style.width = (width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px'; this._modifiedDomNode.style.left = splitPoint + 'px'; this._overviewDomElement.style.top = '0px'; @@ -2743,6 +2746,9 @@ function validateDiffEditorOptions(options: Readonly, defaul collapseUnchangedRegions: false, }, isInEmbeddedEditor: validateBooleanOption(options.isInEmbeddedEditor, defaults.isInEmbeddedEditor), + onlyShowAccessibleDiffViewer: false, + renderSideBySideInlineBreakpoint: 0, + useInlineViewWhenSpaceIsLimited: false, }; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts new file mode 100644 index 00000000000..315ee1856ea --- /dev/null +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -0,0 +1,688 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { addDisposableListener, addStandardDisposableListener, reset } from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Action } from 'vs/base/common/actions'; +import { Codicon } from 'vs/base/common/codicons'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { IObservable, ITransaction, autorun, autorunWithStore, derived, derivedWithStore, keepAlive, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; +import { applyStyle } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { DiffReview } from 'vs/editor/browser/widget/diffReview'; +import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { LineRangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { ILanguageIdCodec } from 'vs/editor/common/languages'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; +import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; +import { RenderLineInput, renderViewLine2 } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { ViewLineRenderingData } from 'vs/editor/common/viewModel'; +import { localize } from 'vs/nls'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +const accessibleDiffViewerInsertIcon = registerIcon('diff-review-insert', Codicon.add, localize('accessibleDiffViewerInsertIcon', 'Icon for \'Insert\' in accessible diff viewer.')); +const accessibleDiffViewerRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, localize('accessibleDiffViewerRemoveIcon', 'Icon for \'Remove\' in accessible diff viewer.')); +const accessibleDiffViewerCloseIcon = registerIcon('diff-review-close', Codicon.close, localize('accessibleDiffViewerCloseIcon', 'Icon for \'Close\' in accessible diff viewer.')); + +export class AccessibleDiffViewer extends Disposable { + constructor( + private readonly _parentNode: HTMLElement, + private readonly _visible: IObservable, + private readonly _setVisible: (visible: boolean, tx: ITransaction | undefined) => void, + private readonly _canClose: IObservable, + private readonly _width: IObservable, + private readonly _height: IObservable, + private readonly _diffs: IObservable, + private readonly _editors: DiffEditorEditors, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + this._register(keepAlive(this.model, true)); + } + + private readonly model = derivedWithStore('model', (reader, store) => { + const visible = this._visible.read(reader); + this._parentNode.style.visibility = visible ? 'visible' : 'hidden'; + if (!visible) { + return null; + } + const model = store.add(this._instantiationService.createInstance(ViewModel, this._diffs, this._editors, this._setVisible, this._canClose)); + const view = store.add(this._instantiationService.createInstance(View, this._parentNode, model, this._width, this._height, this._editors)); + return { + model, + view + }; + }); + + next(): void { + transaction(tx => { + const isVisible = this._visible.get(); + this._setVisible(true, tx); + if (isVisible) { + this.model.get()!.model.nextGroup(tx); + } + }); + } + + prev(): void { + transaction(tx => { + this._setVisible(true, tx); + this.model.get()!.model.previousGroup(tx); + }); + } + + close(): void { + transaction(tx => { + this._setVisible(false, tx); + }); + } +} + +class ViewModel extends Disposable { + private readonly _groups = observableValue('groups', []); + private readonly _currentGroupIdx = observableValue('currentGroupIdx', 0); + private readonly _currentElementIdx = observableValue('currentElementIdx', 0); + + public readonly groups: IObservable = this._groups; + public readonly currentGroup: IObservable + = this._currentGroupIdx.map((idx, r) => this._groups.read(r)[idx]); + public readonly currentGroupIndex: IObservable = this._currentGroupIdx; + + public readonly currentElement: IObservable + = this._currentElementIdx.map((idx, r) => this.currentGroup.read(r)?.lines[idx]); + + constructor( + private readonly _diffs: IObservable, + private readonly _editors: DiffEditorEditors, + private readonly _setVisible: (visible: boolean, tx: ITransaction | undefined) => void, + public readonly canClose: IObservable, + @IAudioCueService private readonly _audioCueService: IAudioCueService, + ) { + super(); + + this._register(autorun(reader => { + /** @description update groups */ + const diffs = this._diffs.read(reader); + if (!diffs) { + this._groups.set([], undefined); + return; + } + + const groups = computeViewElementGroups( + diffs, + this._editors.original.getModel()!.getLineCount(), + this._editors.modified.getModel()!.getLineCount() + ); + + transaction(tx => { + const p = this._editors.modified.getPosition(); + if (p) { + const nextGroup = groups.findIndex(g => p?.lineNumber < g.range.modified.endLineNumberExclusive); + if (nextGroup !== -1) { + this._currentGroupIdx.set(nextGroup, tx); + } + } + this._groups.set(groups, tx); + }); + })); + + this._register(autorun(reader => { + /** @description play audio-cue for diff */ + const currentViewItem = this.currentElement.read(reader); + if (currentViewItem?.type === LineType.Deleted) { + this._audioCueService.playAudioCue(AudioCue.diffLineDeleted); + } else if (currentViewItem?.type === LineType.Added) { + this._audioCueService.playAudioCue(AudioCue.diffLineInserted); + } + })); + + this._register(autorun(reader => { + /** @description select lines in editor */ + // This ensures editor commands (like revert/stage) work + const currentViewItem = this.currentElement.read(reader); + if (currentViewItem && currentViewItem.type !== LineType.Header) { + const lineNumber = currentViewItem.modifiedLineNumber ?? currentViewItem.diff.modifiedRange.startLineNumber; + this._editors.modified.setSelection(Range.fromPositions(new Position(lineNumber, 1))); + } + })); + } + + private _goToGroupDelta(delta: number, tx?: ITransaction): void { + const groups = this.groups.get(); + if (!groups || groups.length <= 1) { return; } + subtransaction(tx, tx => { + this._currentGroupIdx.set((this._currentGroupIdx.get() + groups.length + delta) % groups.length, tx); + this._currentElementIdx.set(0, tx); + }); + } + + nextGroup(tx?: ITransaction): void { this._goToGroupDelta(1, tx); } + previousGroup(tx?: ITransaction): void { this._goToGroupDelta(-1, tx); } + + private _goToLineDelta(delta: number): void { + const group = this.currentGroup.get(); + if (!group || group.lines.length <= 1) { return; } + transaction(tx => { + this._currentElementIdx.set((this._currentElementIdx.get() + group.lines.length + delta) % group.lines.length, tx); + }); + } + + goToNextLine(): void { this._goToLineDelta(1); } + goToPreviousLine(): void { this._goToLineDelta(-1); } + + goToLine(line: ViewElement): void { + const group = this.currentGroup.get(); + if (!group) { return; } + const idx = group.lines.indexOf(line); + if (idx === -1) { return; } + transaction(tx => { + this._currentElementIdx.set(idx, tx); + }); + } + + revealCurrentElementInEditor(): void { + this._setVisible(false, undefined); + + const curElem = this.currentElement.get(); + if (curElem) { + if (curElem.type === LineType.Deleted) { + this._editors.original.setSelection(Range.fromPositions(new Position(curElem.originalLineNumber, 1))); + this._editors.original.revealLine(curElem.originalLineNumber); + this._editors.original.focus(); + } else { + if (curElem.type !== LineType.Header) { + this._editors.modified.setSelection(Range.fromPositions(new Position(curElem.modifiedLineNumber, 1))); + this._editors.modified.revealLine(curElem.modifiedLineNumber); + } + this._editors.modified.focus(); + } + } + } + + close(): void { + this._setVisible(false, undefined); + this._editors.modified.focus(); + } +} + + +const viewElementGroupLineMargin = 3; + +function computeViewElementGroups(diffs: LineRangeMapping[], originalLineCount: number, modifiedLineCount: number): ViewElementGroup[] { + const result: ViewElementGroup[] = []; + + for (const g of group(diffs, (a, b) => (b.modifiedRange.startLineNumber - a.modifiedRange.endLineNumberExclusive < 2 * viewElementGroupLineMargin))) { + const viewElements: ViewElement[] = []; + viewElements.push(new HeaderViewElement()); + + const origFullRange = new LineRange( + Math.max(1, g[0].originalRange.startLineNumber - viewElementGroupLineMargin), + Math.min(g[g.length - 1].originalRange.endLineNumberExclusive + viewElementGroupLineMargin, originalLineCount + 1) + ); + const modifiedFullRange = new LineRange( + Math.max(1, g[0].modifiedRange.startLineNumber - viewElementGroupLineMargin), + Math.min(g[g.length - 1].modifiedRange.endLineNumberExclusive + viewElementGroupLineMargin, modifiedLineCount + 1) + ); + + forEachAdjacentItems(g, (a, b) => { + const origRange = new LineRange(a ? a.originalRange.endLineNumberExclusive : origFullRange.startLineNumber, b ? b.originalRange.startLineNumber : origFullRange.endLineNumberExclusive); + const modifiedRange = new LineRange(a ? a.modifiedRange.endLineNumberExclusive : modifiedFullRange.startLineNumber, b ? b.modifiedRange.startLineNumber : modifiedFullRange.endLineNumberExclusive); + + origRange.forEach(origLineNumber => { + viewElements.push(new UnchangedLineViewElement(origLineNumber, modifiedRange.startLineNumber + (origLineNumber - origRange.startLineNumber))); + }); + + if (b) { + b.originalRange.forEach(origLineNumber => { + viewElements.push(new DeletedLineViewElement(b, origLineNumber)); + }); + b.modifiedRange.forEach(modifiedLineNumber => { + viewElements.push(new AddedLineViewElement(b, modifiedLineNumber)); + }); + } + }); + + const modifiedRange = g[0].modifiedRange.join(g[g.length - 1].modifiedRange); + const originalRange = g[0].originalRange.join(g[g.length - 1].originalRange); + + result.push(new ViewElementGroup(new SimpleLineRangeMapping(modifiedRange, originalRange), viewElements)); + } + return result; +} + +enum LineType { + Header, + Unchanged, + Deleted, + Added, +} + +class ViewElementGroup { + constructor( + public readonly range: SimpleLineRangeMapping, + public readonly lines: readonly ViewElement[], + ) { } +} + +type ViewElement = HeaderViewElement | UnchangedLineViewElement | DeletedLineViewElement | AddedLineViewElement; + +class HeaderViewElement { + public readonly type = LineType.Header; +} + +class DeletedLineViewElement { + public readonly type = LineType.Deleted; + + public readonly modifiedLineNumber = undefined; + + constructor( + public readonly diff: LineRangeMapping, + public readonly originalLineNumber: number, + ) { + } +} + +class AddedLineViewElement { + public readonly type = LineType.Added; + + public readonly originalLineNumber = undefined; + + constructor( + public readonly diff: LineRangeMapping, + public readonly modifiedLineNumber: number, + ) { + } +} + +class UnchangedLineViewElement { + public readonly type = LineType.Unchanged; + constructor( + public readonly originalLineNumber: number, + public readonly modifiedLineNumber: number, + ) { + } +} + +class View extends Disposable { + public readonly domNode: HTMLElement; + private readonly _content: HTMLElement; + private readonly _scrollbar: DomScrollableElement; + private readonly _actionBar: ActionBar; + + constructor( + private readonly _element: HTMLElement, + private readonly _model: ViewModel, + private readonly _width: IObservable, + private readonly _height: IObservable, + private readonly _editors: DiffEditorEditors, + @ILanguageService private readonly _languageService: ILanguageService, + ) { + super(); + + this.domNode = this._element; + this.domNode.className = 'diff-review monaco-editor-background'; + + const actionBarContainer = document.createElement('div'); + actionBarContainer.className = 'diff-review-actions'; + this._actionBar = this._register(new ActionBar( + actionBarContainer + )); + this._register(autorun(reader => { + /** @description update actions */ + this._actionBar.clear(); + if (this._model.canClose.read(reader)) { + this._actionBar.push(new Action( + 'diffreview.close', + localize('label.close', "Close"), + 'close-diff-review ' + ThemeIcon.asClassName(accessibleDiffViewerCloseIcon), + true, + async () => _model.close() + ), { label: false, icon: true }); + } + })); + + this._content = document.createElement('div'); + this._content.className = 'diff-review-content'; + this._content.setAttribute('role', 'code'); + this._scrollbar = this._register(new DomScrollableElement(this._content, {})); + reset(this.domNode, this._scrollbar.getDomNode(), actionBarContainer); + + this._register(toDisposable(() => { reset(this.domNode); })); + + this._register(applyStyle(this.domNode, { width: this._width, height: this._height })); + this._register(applyStyle(this._content, { width: this._width, height: this._height })); + + this._register(autorunWithStore((reader, store) => { + /** @description render */ + this._model.currentGroup.read(reader); + this._render(store); + })); + + // TODO@hediet use commands + this._register(addStandardDisposableListener(this.domNode, 'keydown', (e) => { + if ( + e.equals(KeyCode.DownArrow) + || e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow) + || e.equals(KeyMod.Alt | KeyCode.DownArrow) + ) { + e.preventDefault(); + this._model.goToNextLine(); + } + + if ( + e.equals(KeyCode.UpArrow) + || e.equals(KeyMod.CtrlCmd | KeyCode.UpArrow) + || e.equals(KeyMod.Alt | KeyCode.UpArrow) + ) { + e.preventDefault(); + this._model.goToPreviousLine(); + } + + if ( + e.equals(KeyCode.Escape) + || e.equals(KeyMod.CtrlCmd | KeyCode.Escape) + || e.equals(KeyMod.Alt | KeyCode.Escape) + || e.equals(KeyMod.Shift | KeyCode.Escape) + ) { + e.preventDefault(); + this._model.close(); + } + + if ( + e.equals(KeyCode.Space) + || e.equals(KeyCode.Enter) + ) { + e.preventDefault(); + this._model.revealCurrentElementInEditor(); + } + })); + } + + private _render(store: DisposableStore): void { + const originalOptions = this._editors.original.getOptions(); + const modifiedOptions = this._editors.modified.getOptions(); + + const container = document.createElement('div'); + container.className = 'diff-review-table'; + container.setAttribute('role', 'list'); + container.setAttribute('aria-label', localize('ariaLabel', 'Accessible Diff Viewer. Use arrow up and down to navigate.')); + applyFontInfo(container, modifiedOptions.get(EditorOption.fontInfo)); + + reset(this._content, container); + + const originalModel = this._editors.original.getModel(); + const modifiedModel = this._editors.modified.getModel(); + if (!originalModel || !modifiedModel) { + return; + } + + const originalModelOpts = originalModel.getOptions(); + const modifiedModelOpts = modifiedModel.getOptions(); + + const lineHeight = modifiedOptions.get(EditorOption.lineHeight); + const group = this._model.currentGroup.get(); + for (const viewItem of group?.lines || []) { + if (!group) { + break; + } + let row: HTMLDivElement; + + if (viewItem.type === LineType.Header) { + + const header = document.createElement('div'); + header.className = 'diff-review-row'; + header.setAttribute('role', 'listitem'); + + const r = group.range; + const diffIndex = this._model.currentGroupIndex.get(); + const diffsLength = this._model.groups.get().length; + const getAriaLines = (lines: number) => + lines === 0 ? localize('no_lines_changed', "no lines changed") + : lines === 1 ? localize('one_line_changed', "1 line changed") + : localize('more_lines_changed', "{0} lines changed", lines); + + const originalChangedLinesCntAria = getAriaLines(r.original.length); + const modifiedChangedLinesCntAria = getAriaLines(r.modified.length); + header.setAttribute('aria-label', localize({ + key: 'header', + comment: [ + 'This is the ARIA label for a git diff header.', + 'A git diff header looks like this: @@ -154,12 +159,39 @@.', + 'That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.', + 'Variables 0 and 1 refer to the diff index out of total number of diffs.', + 'Variables 2 and 4 will be numbers (a line number).', + 'Variables 3 and 5 will be "no lines changed", "1 line changed" or "X lines changed", localized separately.' + ] + }, "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", + (diffIndex + 1), + diffsLength, + r.original.startLineNumber, + originalChangedLinesCntAria, + r.modified.startLineNumber, + modifiedChangedLinesCntAria + )); + + const cell = document.createElement('div'); + cell.className = 'diff-review-cell diff-review-summary'; + // e.g.: `1/10: @@ -504,7 +517,7 @@` + cell.appendChild(document.createTextNode(`${diffIndex + 1}/${diffsLength}: @@ -${r.original.startLineNumber},${r.original.length} +${r.modified.startLineNumber},${r.modified.length} @@`)); + header.appendChild(cell); + + row = header; + } else { + row = this._createRow(viewItem, lineHeight, + this._width.get(), originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts, + ); + } + + container.appendChild(row); + + const isSelectedObs = derived(reader => /** @description isSelected */ this._model.currentElement.read(reader) === viewItem); + + store.add(autorun(reader => { + /** @description update tab index */ + const isSelected = isSelectedObs.read(reader); + row.tabIndex = isSelected ? 0 : -1; + if (isSelected) { + row.focus(); + } + })); + + store.add(addDisposableListener(row, 'focus', () => { + this._model.goToLine(viewItem); + })); + } + + this._scrollbar.scanDomNode(); + } + + private _createRow( + item: DeletedLineViewElement | AddedLineViewElement | UnchangedLineViewElement, + lineHeight: number, + width: number, + originalOptions: IComputedEditorOptions, originalModel: ITextModel, originalModelOpts: TextModelResolvedOptions, + modifiedOptions: IComputedEditorOptions, modifiedModel: ITextModel, modifiedModelOpts: TextModelResolvedOptions, + ): HTMLDivElement { + const originalLayoutInfo = originalOptions.get(EditorOption.layoutInfo); + const originalLineNumbersWidth = originalLayoutInfo.glyphMarginWidth + originalLayoutInfo.lineNumbersWidth; + + const modifiedLayoutInfo = modifiedOptions.get(EditorOption.layoutInfo); + const modifiedLineNumbersWidth = 10 + modifiedLayoutInfo.glyphMarginWidth + modifiedLayoutInfo.lineNumbersWidth; + + let rowClassName: string = 'diff-review-row'; + let lineNumbersExtraClassName: string = ''; + const spacerClassName: string = 'diff-review-spacer'; + let spacerIcon: ThemeIcon | null = null; + switch (item.type) { + case LineType.Added: + rowClassName = 'diff-review-row line-insert'; + lineNumbersExtraClassName = ' char-insert'; + spacerIcon = accessibleDiffViewerInsertIcon; + break; + case LineType.Deleted: + rowClassName = 'diff-review-row line-delete'; + lineNumbersExtraClassName = ' char-delete'; + spacerIcon = accessibleDiffViewerRemoveIcon; + break; + } + + const row = document.createElement('div'); + row.style.minWidth = width + 'px'; + row.className = rowClassName; + row.setAttribute('role', 'listitem'); + row.ariaLevel = ''; + + const cell = document.createElement('div'); + cell.className = 'diff-review-cell'; + cell.style.height = `${lineHeight}px`; + row.appendChild(cell); + + const originalLineNumber = document.createElement('span'); + originalLineNumber.style.width = (originalLineNumbersWidth + 'px'); + originalLineNumber.style.minWidth = (originalLineNumbersWidth + 'px'); + originalLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; + if (item.originalLineNumber !== undefined) { + originalLineNumber.appendChild(document.createTextNode(String(item.originalLineNumber))); + } else { + originalLineNumber.innerText = '\u00a0'; + } + cell.appendChild(originalLineNumber); + + const modifiedLineNumber = document.createElement('span'); + modifiedLineNumber.style.width = (modifiedLineNumbersWidth + 'px'); + modifiedLineNumber.style.minWidth = (modifiedLineNumbersWidth + 'px'); + modifiedLineNumber.style.paddingRight = '10px'; + modifiedLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; + if (item.modifiedLineNumber !== undefined) { + modifiedLineNumber.appendChild(document.createTextNode(String(item.modifiedLineNumber))); + } else { + modifiedLineNumber.innerText = '\u00a0'; + } + cell.appendChild(modifiedLineNumber); + + const spacer = document.createElement('span'); + spacer.className = spacerClassName; + + if (spacerIcon) { + const spacerCodicon = document.createElement('span'); + spacerCodicon.className = ThemeIcon.asClassName(spacerIcon); + spacerCodicon.innerText = '\u00a0\u00a0'; + spacer.appendChild(spacerCodicon); + } else { + spacer.innerText = '\u00a0\u00a0'; + } + cell.appendChild(spacer); + + let lineContent: string; + if (item.modifiedLineNumber !== undefined) { + let html: string | TrustedHTML = this._getLineHtml(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, item.modifiedLineNumber, this._languageService.languageIdCodec); + if (DiffReview._ttPolicy) { + html = DiffReview._ttPolicy.createHTML(html as string); + } + cell.insertAdjacentHTML('beforeend', html as string); + lineContent = modifiedModel.getLineContent(item.modifiedLineNumber); + } else { + let html: string | TrustedHTML = this._getLineHtml(originalModel, originalOptions, originalModelOpts.tabSize, item.originalLineNumber, this._languageService.languageIdCodec); + if (DiffReview._ttPolicy) { + html = DiffReview._ttPolicy.createHTML(html as string); + } + cell.insertAdjacentHTML('beforeend', html as string); + lineContent = originalModel.getLineContent(item.originalLineNumber); + } + + if (lineContent.length === 0) { + lineContent = localize('blankLine', "blank"); + } + + let ariaLabel: string = ''; + switch (item.type) { + case LineType.Unchanged: + if (item.originalLineNumber === item.modifiedLineNumber) { + ariaLabel = localize({ key: 'unchangedLine', comment: ['The placeholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, item.originalLineNumber); + } else { + ariaLabel = localize('equalLine', "{0} original line {1} modified line {2}", lineContent, item.originalLineNumber, item.modifiedLineNumber); + } + break; + case LineType.Added: + ariaLabel = localize('insertLine', "+ {0} modified line {1}", lineContent, item.modifiedLineNumber); + break; + case LineType.Deleted: + ariaLabel = localize('deleteLine', "- {0} original line {1}", lineContent, item.originalLineNumber); + break; + } + row.setAttribute('aria-label', ariaLabel); + + return row; + } + + private _getLineHtml(model: ITextModel, options: IComputedEditorOptions, tabSize: number, lineNumber: number, languageIdCodec: ILanguageIdCodec): string { + const lineContent = model.getLineContent(lineNumber); + const fontInfo = options.get(EditorOption.fontInfo); + const lineTokens = LineTokens.createEmpty(lineContent, languageIdCodec); + const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, model.mightContainNonBasicASCII()); + const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL()); + const r = renderViewLine2(new RenderLineInput( + (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), + fontInfo.canUseHalfwidthRightwardsArrow, + lineContent, + false, + isBasicASCII, + containsRTL, + 0, + lineTokens, + [], + tabSize, + 0, + fontInfo.spaceWidth, + fontInfo.middotWidth, + fontInfo.wsmiddotWidth, + options.get(EditorOption.stopRenderingLineAfter), + options.get(EditorOption.renderWhitespace), + options.get(EditorOption.renderControlCharacters), + options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, + null + )); + + return r.html; + } +} + +function forEachAdjacentItems(items: T[], callback: (item1: T | undefined, item2: T | undefined) => void) { + let last: T | undefined; + for (const item of items) { + callback(last, item); + last = item; + } + callback(last, undefined); +} + +function* group(items: Iterable, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable { + let currentGroup: T[] | undefined; + let last: T | undefined; + for (const item of items) { + if (last !== undefined && shouldBeGrouped(last, item)) { + currentGroup!.push(item); + } else { + if (currentGroup) { + yield currentGroup; + } + currentGroup = [item]; + } + last = item; + } + if (currentGroup) { + yield currentGroup; + } +} diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts index bc56c23694d..350bbd58120 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts @@ -80,7 +80,8 @@ export const diffDeleteDecorationEmpty = ModelDecorationOptions.register({ export const arrowRevertChange = ModelDecorationOptions.register({ description: 'diff-editor-arrow-revert-change', - glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendMarkdown(localize('revertChangeHoverMessage', 'Click to revert change')), + glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }) + .appendMarkdown(localize('revertChangeHoverMessage', 'Click to revert change')), glyphMarginClassName: 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight), zIndex: 10001, }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 78f20246013..5d9af4098f9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -6,7 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, derived } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; -import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; +import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; @@ -29,7 +29,8 @@ export class DiffEditorDecorations extends Disposable { this._register(applyObservableDecorations(this._editors.modified, this._decorations.map(d => d?.modifiedDecorations || []))); } - private readonly _decorations = derived('decorations', (reader) => { + private readonly _decorations = derived((reader) => { + /** @description _decorations */ const diff = this._diffModel.read(reader)?.diff.read(reader); if (!diff) { return null; @@ -42,31 +43,40 @@ export class DiffEditorDecorations extends Disposable { const originalDecorations: IModelDeltaDecoration[] = []; const modifiedDecorations: IModelDeltaDecoration[] = []; for (const m of diff.mappings) { - const fullRangeOriginal = LineRange.subtract(m.lineRangeMapping.originalRange, currentMove?.lineRangeMapping.originalRange) + const fullRangeOriginal = LineRange.subtract(m.lineRangeMapping.originalRange, currentMove?.lineRangeMapping.original) .map(i => i.toInclusiveRange()).filter(isDefined); for (const range of fullRangeOriginal) { originalDecorations.push({ range, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); } - const fullRangeModified = LineRange.subtract(m.lineRangeMapping.modifiedRange, currentMove?.lineRangeMapping.modifiedRange) + const fullRangeModified = LineRange.subtract(m.lineRangeMapping.modifiedRange, currentMove?.lineRangeMapping.modified) .map(i => i.toInclusiveRange()).filter(isDefined); for (const range of fullRangeModified) { modifiedDecorations.push({ range, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); } - for (const i of m.lineRangeMapping.innerChanges || []) { - if (currentMove - && (currentMove.lineRangeMapping.originalRange.intersect(new LineRange(i.originalRange.startLineNumber, i.originalRange.endLineNumber)) - || currentMove.lineRangeMapping.modifiedRange.intersect(new LineRange(i.modifiedRange.startLineNumber, i.modifiedRange.endLineNumber)))) { - continue; + if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) { + for (const range of fullRangeOriginal) { + originalDecorations.push({ range, options: diffWholeLineDeleteDecoration }); } + for (const range of fullRangeModified) { + modifiedDecorations.push({ range, options: diffWholeLineAddDecoration }); + } + } else { + for (const i of m.lineRangeMapping.innerChanges || []) { + if (currentMove + && (currentMove.lineRangeMapping.original.intersect(new LineRange(i.originalRange.startLineNumber, i.originalRange.endLineNumber)) + || currentMove.lineRangeMapping.modified.intersect(new LineRange(i.modifiedRange.startLineNumber, i.modifiedRange.endLineNumber)))) { + continue; + } - // Don't show empty markers outside the line range - if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { - originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); - } - if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { - modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); + // Don't show empty markers outside the line range + if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { + originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); + } + if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { + modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); + } } } @@ -95,7 +105,7 @@ export class DiffEditorDecorations extends Disposable { for (const m of diff.movedTexts) { originalDecorations.push({ - range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: { + range: m.lineRangeMapping.original.toInclusiveRange()!, options: { description: 'moved', blockClassName: 'movedOriginal', blockPadding: [MovedBlocksLinesPart.movedCodeBlockPadding, 0, MovedBlocksLinesPart.movedCodeBlockPadding, MovedBlocksLinesPart.movedCodeBlockPadding], @@ -103,7 +113,7 @@ export class DiffEditorDecorations extends Disposable { }); modifiedDecorations.push({ - range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: { + range: m.lineRangeMapping.modified.toInclusiveRange()!, options: { description: 'moved', blockClassName: 'movedModified', blockPadding: [4, 0, 4, 4], diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index a3c39014157..cce99a94271 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { autorunHandleChanges } from 'vs/base/common/observableImpl/autorun'; +import { IReader, autorunHandleChanges } from 'vs/base/common/observable'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -13,8 +13,8 @@ import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/ov import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DiffEditorOptions } from './diffEditorOptions'; export class DiffEditorEditors extends Disposable { @@ -30,15 +30,15 @@ export class DiffEditorEditors extends Disposable { private readonly _options: DiffEditorOptions, codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, private readonly _createInnerEditor: (instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions) => CodeEditorWidget, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { super(); this.original = this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}); this.modified = this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}); - this._register(autorunHandleChanges('update editor options', { + this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), handleChange: (ctx, changeSummary) => { if (ctx.didChange(_options.editorOptions)) { @@ -47,26 +47,25 @@ export class DiffEditorEditors extends Disposable { return true; } }, (reader, changeSummary) => { + /** @description update editor options */ _options.editorOptions.read(reader); - this.modified.updateOptions(this._adjustOptionsForRightHandSide(changeSummary)); - this.original.updateOptions(this._adjustOptionsForLeftHandSide(changeSummary)); + this.modified.updateOptions(this._adjustOptionsForRightHandSide(reader, changeSummary)); + this.original.updateOptions(this._adjustOptionsForLeftHandSide(reader, changeSummary)); })); } private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions); - const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus()); - this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + const leftHandSideOptions = this._adjustOptionsForLeftHandSide(undefined, options); + const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, leftHandSideOptions, codeEditorWidgetOptions); + editor.setContextValue('isInDiffLeftEditor', true); return editor; } private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions); - const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus()); - this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); + const rightHandSideOptions = this._adjustOptionsForRightHandSide(undefined, options); + const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, rightHandSideOptions, codeEditorWidgetOptions); + editor.setContextValue('isInDiffRightEditor', true); return editor; } @@ -87,7 +86,7 @@ export class DiffEditorEditors extends Disposable { return editor; } - private _adjustOptionsForLeftHandSide(changedOptions: Readonly): IEditorConstructionOptions { + private _adjustOptionsForLeftHandSide(_reader: IReader | undefined, changedOptions: Readonly): IEditorConstructionOptions { const result = this._adjustOptionsForSubEditor(changedOptions); if (!this._options.renderSideBySide.get()) { // never wrap hidden editor @@ -107,7 +106,7 @@ export class DiffEditorEditors extends Disposable { return result; } - private _adjustOptionsForRightHandSide(changedOptions: Readonly): IEditorConstructionOptions { + private _adjustOptionsForRightHandSide(reader: IReader | undefined, changedOptions: Readonly): IEditorConstructionOptions { const result = this._adjustOptionsForSubEditor(changedOptions); if (changedOptions.modifiedAriaLabel) { result.ariaLabel = changedOptions.modifiedAriaLabel; @@ -130,13 +129,14 @@ export class DiffEditorEditors extends Disposable { }; clonedOptions.inDiffEditor = true; clonedOptions.automaticLayout = false; + // Clone scrollbar options before changing them clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) }; clonedOptions.scrollbar.vertical = 'visible'; clonedOptions.folding = false; clonedOptions.codeLens = this._options.diffCodeLens.get(); clonedOptions.fixedOverflowWidgets = true; - // clonedOptions.lineDecorationsWidth = '2ch'; + // Clone minimap options before changing them clonedOptions.minimap = { ...(clonedOptions.minimap || {}) }; clonedOptions.minimap.enabled = false; @@ -150,12 +150,15 @@ export class DiffEditorEditors extends Disposable { } private _updateAriaLabel(ariaLabel: string | undefined): string | undefined { - const ariaNavigationTip = localize('diff-aria-navigation-tip', ' use Shift + F7 to navigate changes'); + if (!ariaLabel) { + ariaLabel = ''; + } + const ariaNavigationTip = localize('diff-aria-navigation-tip', ' use {0} to open the accessibility help.', this._keybindingService.lookupKeybinding('editor.action.accessibilityHelp')?.getAriaLabel()); if (this._options.accessibilityVerbose.get()) { return ariaLabel + ariaNavigationTip; } else if (ariaLabel) { return ariaLabel.replaceAll(ariaNavigationTip, ''); } - return undefined; + return ''; } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts index fcec4e86f09..ee8aa4e9ca0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts @@ -14,38 +14,46 @@ export class DiffEditorOptions { public get editorOptions(): IObservable { return this._options; } - constructor(options: Readonly) { + constructor(options: Readonly, private readonly diffEditorWidth: IObservable) { const optionsCopy = { ...options, ...validateDiffEditorOptions(options, diffEditorDefaultOptions) }; this._options = observableValue('options', optionsCopy); } - public readonly renderOverviewRuler = derived('renderOverviewRuler', reader => this._options.read(reader).renderOverviewRuler); - public readonly renderSideBySide = derived('renderSideBySide', reader => this._options.read(reader).renderSideBySide); - public readonly readOnly = derived('readOnly', reader => this._options.read(reader).readOnly); + public readonly couldShowInlineViewBecauseOfSize = derived(reader => /** @description couldShowInlineViewBecauseOfSize */ this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint + ); - public readonly shouldRenderRevertArrows = derived('shouldRenderRevertArrows', (reader) => { + public readonly renderOverviewRuler = derived(reader => /** @description renderOverviewRuler */ this._options.read(reader).renderOverviewRuler); + public readonly renderSideBySide = derived(reader => /** @description renderSideBySide */ this._options.read(reader).renderSideBySide + && !(this._options.read(reader).useInlineViewWhenSpaceIsLimited && this.couldShowInlineViewBecauseOfSize.read(reader)) + ); + public readonly readOnly = derived(reader => /** @description readOnly */ this._options.read(reader).readOnly); + + public readonly shouldRenderRevertArrows = derived(reader => { + /** @description shouldRenderRevertArrows */ if (!this._options.read(reader).renderMarginRevertIcon) { return false; } if (!this.renderSideBySide.read(reader)) { return false; } if (this.readOnly.read(reader)) { return false; } return true; }); - public readonly renderIndicators = derived('renderIndicators', reader => this._options.read(reader).renderIndicators); - public readonly enableSplitViewResizing = derived('enableSplitViewResizing', reader => this._options.read(reader).enableSplitViewResizing); - public readonly collapseUnchangedRegions = derived('hideUnchangedRegions', reader => this._options.read(reader).experimental.collapseUnchangedRegions!); - public readonly splitViewDefaultRatio = derived('splitViewDefaultRatio', reader => this._options.read(reader).splitViewDefaultRatio); - public readonly ignoreTrimWhitespace = derived('ignoreTrimWhitespace', reader => this._options.read(reader).ignoreTrimWhitespace); - public readonly maxComputationTimeMs = derived('maxComputationTime', reader => this._options.read(reader).maxComputationTime); - public readonly showMoves = derived('showMoves', reader => { + public readonly renderIndicators = derived(reader => /** @description renderIndicators */ this._options.read(reader).renderIndicators); + public readonly enableSplitViewResizing = derived(reader => /** @description enableSplitViewResizing */ this._options.read(reader).enableSplitViewResizing); + public readonly collapseUnchangedRegions = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).experimental.collapseUnchangedRegions!); + public readonly splitViewDefaultRatio = derived(reader => /** @description splitViewDefaultRatio */ this._options.read(reader).splitViewDefaultRatio); + public readonly ignoreTrimWhitespace = derived(reader => /** @description ignoreTrimWhitespace */ this._options.read(reader).ignoreTrimWhitespace); + public readonly maxComputationTimeMs = derived(reader => /** @description maxComputationTime */ this._options.read(reader).maxComputationTime); + public readonly showMoves = derived(reader => { + /** @description showMoves */ const o = this._options.read(reader); return o.experimental.showMoves! && o.renderSideBySide; }); - public readonly isInEmbeddedEditor = derived('isInEmbeddedEditor', reader => this._options.read(reader).isInEmbeddedEditor); - public readonly diffWordWrap = derived('diffWordWrap', reader => this._options.read(reader).diffWordWrap); - public readonly originalEditable = derived('originalEditable', reader => this._options.read(reader).originalEditable); - public readonly diffCodeLens = derived('diffCodeLens', reader => this._options.read(reader).diffCodeLens); - public readonly accessibilityVerbose = derived('accessibilityVerbose', reader => this._options.read(reader).accessibilityVerbose); - public readonly diffAlgorithm = derived('diffAlgorithm', reader => this._options.read(reader).diffAlgorithm); - public readonly showEmptyDecorations = derived('showEmptyDecorations', reader => this._options.read(reader).experimental.showEmptyDecorations!); + public readonly isInEmbeddedEditor = derived(reader => /** @description isInEmbeddedEditor */ this._options.read(reader).isInEmbeddedEditor); + public readonly diffWordWrap = derived(reader => /** @description diffWordWrap */ this._options.read(reader).diffWordWrap); + public readonly originalEditable = derived(reader => /** @description originalEditable */ this._options.read(reader).originalEditable); + public readonly diffCodeLens = derived(reader => /** @description diffCodeLens */ this._options.read(reader).diffCodeLens); + public readonly accessibilityVerbose = derived(reader => /** @description accessibilityVerbose */ this._options.read(reader).accessibilityVerbose); + public readonly diffAlgorithm = derived(reader => /** @description diffAlgorithm */ this._options.read(reader).diffAlgorithm); + public readonly showEmptyDecorations = derived(reader => /** @description showEmptyDecorations */ this._options.read(reader).experimental.showEmptyDecorations!); + public readonly onlyShowAccessibleDiffViewer = derived(reader => /** @description onlyShowAccessibleDiffViewer */ this._options.read(reader).onlyShowAccessibleDiffViewer); public updateOptions(changedOptions: IDiffEditorOptions): void { const newDiffEditorOptions = validateDiffEditorOptions(changedOptions, this._options.get()); @@ -75,6 +83,9 @@ const diffEditorDefaultOptions: ValidDiffEditorBaseOptions = { showEmptyDecorations: true, }, isInEmbeddedEditor: false, + onlyShowAccessibleDiffViewer: false, + renderSideBySideInlineBreakpoint: 900, + useInlineViewWhenSpaceIsLimited: true, }; function validateDiffEditorOptions(options: Readonly, defaults: ValidDiffEditorBaseOptions): ValidDiffEditorBaseOptions { @@ -99,5 +110,8 @@ function validateDiffEditorOptions(options: Readonly, defaul showEmptyDecorations: validateBooleanOption(options.experimental?.showEmptyDecorations, defaults.experimental.showEmptyDecorations!), }, isInEmbeddedEditor: validateBooleanOption(options.isInEmbeddedEditor, defaults.isInEmbeddedEditor), + onlyShowAccessibleDiffViewer: validateBooleanOption(options.onlyShowAccessibleDiffViewer, defaults.onlyShowAccessibleDiffViewer), + renderSideBySideInlineBreakpoint: clampedInt(options.renderSideBySideInlineBreakpoint, defaults.renderSideBySideInlineBreakpoint, 0, Constants.MAX_SAFE_SMALL_INTEGER), + useInlineViewWhenSpaceIsLimited: validateBooleanOption(options.useInlineViewWhenSpaceIsLimited, defaults.useInlineViewWhenSpaceIsLimited), }; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts index 953a41f5a0c..aa25a92dacf 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts @@ -11,7 +11,8 @@ import { DiffEditorOptions } from './diffEditorOptions'; export class DiffEditorSash extends Disposable { private readonly _sashRatio = observableValue('sashRatio', undefined); - public readonly sashLeft = derived('sashLeft', reader => { + public readonly sashLeft = derived(reader => { + /** @description sashLeft */ const ratio = this._sashRatio.read(reader) ?? this._options.splitViewDefaultRatio.read(reader); return this._computeSashLeft(ratio, reader); }); @@ -42,7 +43,8 @@ export class DiffEditorSash extends Disposable { this._register(this._sash.onDidEnd(() => this._sash.layout())); this._register(this._sash.onDidReset(() => this._sashRatio.set(undefined, undefined))); - this._register(autorun('update sash layout', (reader) => { + this._register(autorun(reader => { + /** @description update sash layout */ const enabled = this._options.enableSplitViewResizing.read(reader); this._sash.state = enabled ? SashState.Enabled : SashState.Disabled; this.sashLeft.read(reader); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index c7e2cebb687..4833d3de651 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -5,8 +5,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, IReader, ISettableObservable, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; -import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; +import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; @@ -32,7 +31,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo 'unchangedRegion', { regions: [], originalDecorationIds: [], modifiedDecorationIds: [] } ); - public readonly unchangedRegions: IObservable = derived('unchangedRegions', r => { + public readonly unchangedRegions: IObservable = derived(r => { + /** @description unchangedRegions */ if (this._options.collapseUnchangedRegions.read(r)) { return this._unchangedRegions.read(r).regions; } else { @@ -59,6 +59,50 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const contentChangedSignal = observableSignal('contentChangedSignal'); const debouncer = this._register(new RunOnceScheduler(() => contentChangedSignal.trigger(undefined), 200)); + const updateUnchangedRegions = (result: IDocumentDiff, tx: ITransaction) => { + const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount()); + + // Transfer state from cur state + const lastUnchangedRegions = this._unchangedRegions.get(); + const lastUnchangedRegionsOrigRanges = lastUnchangedRegions.originalDecorationIds + .map(id => model.original.getDecorationRange(id)) + .filter(r => !!r) + .map(r => LineRange.fromRange(r!)); + const lastUnchangedRegionsModRanges = lastUnchangedRegions.modifiedDecorationIds + .map(id => model.modified.getDecorationRange(id)) + .filter(r => !!r) + .map(r => LineRange.fromRange(r!)); + + const originalDecorationIds = model.original.deltaDecorations( + lastUnchangedRegions.originalDecorationIds, + newUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, options: { description: 'unchanged' } })) + ); + const modifiedDecorationIds = model.modified.deltaDecorations( + lastUnchangedRegions.modifiedDecorationIds, + newUnchangedRegions.map(r => ({ range: r.modifiedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) + ); + + + for (const r of newUnchangedRegions) { + for (let i = 0; i < lastUnchangedRegions.regions.length; i++) { + if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) + && r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { + r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx); + break; + } + } + } + this._unchangedRegions.set( + { + regions: newUnchangedRegions, + originalDecorationIds, + modifiedDecorationIds + }, + tx + ); + }; + + this._register(model.modified.onDidChangeContent((e) => { const diff = this._diff.get(); if (!diff) { @@ -69,9 +113,12 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified); if (result) { this._lastDiff = result; - this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined); + transaction(tx => { + this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); + updateUnchangedRegions(result, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + }); } debouncer.schedule(); @@ -86,9 +133,12 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified); if (result) { this._lastDiff = result; - this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined); + transaction(tx => { + this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); + updateUnchangedRegions(result, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + }); } debouncer.schedule(); @@ -96,7 +146,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const documentDiffProviderOptionChanged = observableSignalFromEvent('documentDiffProviderOptionChanged', documentDiffProvider.onDidChange); - this._register(autorunWithStore2('compute diff', async (reader, store) => { + this._register(autorunWithStore(async (reader, store) => { + /** @description compute diff */ debouncer.cancel(); contentChangedSignal.read(reader); documentDiffProviderOptionChanged.read(reader); @@ -124,53 +175,16 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result; result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result; - const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount()); - - // Transfer state from cur state - const lastUnchangedRegions = this._unchangedRegions.get(); - const lastUnchangedRegionsOrigRanges = lastUnchangedRegions.originalDecorationIds - .map(id => model.original.getDecorationRange(id)) - .filter(r => !!r) - .map(r => LineRange.fromRange(r!)); - const lastUnchangedRegionsModRanges = lastUnchangedRegions.modifiedDecorationIds - .map(id => model.modified.getDecorationRange(id)) - .filter(r => !!r) - .map(r => LineRange.fromRange(r!)); - - const originalDecorationIds = model.original.deltaDecorations( - lastUnchangedRegions.originalDecorationIds, - newUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, options: { description: 'unchanged' } })) - ); - const modifiedDecorationIds = model.modified.deltaDecorations( - lastUnchangedRegions.modifiedDecorationIds, - newUnchangedRegions.map(r => ({ range: r.modifiedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) - ); transaction(tx => { - for (const r of newUnchangedRegions) { - for (let i = 0; i < lastUnchangedRegions.regions.length; i++) { - if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) - && r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { - r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx); - break; - } - } - } + updateUnchangedRegions(result, tx); this._lastDiff = result; - this._diff.set(DiffState.fromDiffResult(result), tx); + const state = DiffState.fromDiffResult(result); + this._diff.set(state, tx); this._isDiffUpToDate.set(true, tx); const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); - - this._unchangedRegions.set( - { - regions: newUnchangedRegions, - originalDecorationIds, - modifiedDecorationIds - }, - tx - ); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); })); } @@ -288,15 +302,18 @@ export class UnchangedRegion { let modStart = mapping.modifiedRange.startLineNumber; let length = mapping.originalRange.length; - if (origStart === 1 && modStart === 1 && length > minContext + minHiddenLineCount) { - if (length < originalLineCount) { + const atStart = origStart === 1 && modStart === 1; + const atEnd = origStart + length === originalLineCount + 1 && modStart + length === modifiedLineCount + 1; + + if ((atStart || atEnd) && length > minContext + minHiddenLineCount) { + if (atStart && !atEnd) { + length -= minContext; + } + if (atEnd && !atStart) { + origStart += minContext; + modStart += minContext; length -= minContext; } - result.push(new UnchangedRegion(origStart, modStart, length, 0, 0)); - } else if (origStart + length === originalLineCount + 1 && modStart + length === modifiedLineCount + 1 && length > minContext + minHiddenLineCount) { - origStart += minContext; - modStart += minContext; - length -= minContext; result.push(new UnchangedRegion(origStart, modStart, length, 0, 0)); } else if (length > minContext * 2 + minHiddenLineCount) { origStart += minContext; @@ -323,7 +340,7 @@ export class UnchangedRegion { private readonly _visibleLineCountBottom = observableValue('visibleLineCountBottom', 0); public readonly visibleLineCountBottom: ISettableObservable = this._visibleLineCountBottom; - private readonly _shouldHideControls = derived('isVisible', reader => this.visibleLineCountTop.read(reader) + this.visibleLineCountBottom.read(reader) === this.lineCount && !this.isDragged.read(reader)); + private readonly _shouldHideControls = derived(reader => /** @description isVisible */ this.visibleLineCountTop.read(reader) + this.visibleLineCountBottom.read(reader) === this.lineCount && !this.isDragged.read(reader)); public readonly isDragged = observableValue('isDragged', false); @@ -427,9 +444,9 @@ function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig const changes = applyModifiedEditsToLineRangeMappings(diff.changes, textEdits, originalTextModel, modifiedTextModel); const moves = diff.moves.map(m => { - const newModifiedRange = applyEditToLineRange(m.lineRangeMapping.modifiedRange, textEdits); + const newModifiedRange = applyEditToLineRange(m.lineRangeMapping.modified, textEdits); return newModifiedRange ? new MovedText( - new SimpleLineRangeMapping(m.lineRangeMapping.originalRange, newModifiedRange), + new SimpleLineRangeMapping(m.lineRangeMapping.original, newModifiedRange), applyModifiedEditsToLineRangeMappings(m.changes, textEdits, originalTextModel, modifiedTextModel), ) : undefined; }).filter(isDefined); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts index 11543c2c18d..9908e9ad309 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts @@ -5,12 +5,16 @@ import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; -import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { findFocusedDiffEditor } from 'vs/editor/browser/widget/diffEditor.contribution'; +import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import './colors'; export class ToggleCollapseUnchangedRegions extends Action2 { constructor() { @@ -77,6 +81,42 @@ export class ToggleShowMovedCodeBlocks extends Action2 { registerAction2(ToggleShowMovedCodeBlocks); +export class ToggleUseInlineViewWhenSpaceIsLimited extends Action2 { + constructor() { + super({ + id: 'diffEditor.toggleUseInlineViewWhenSpaceIsLimited', + title: { value: localize('toggleUseInlineViewWhenSpaceIsLimited', "Toggle Use Inline View When Space Is Limited"), original: 'Toggle Use Inline View When Space Is Limited' }, + precondition: ContextKeyEqualsExpr.create('diffEditorVersion', 2), + }); + } + + run(accessor: ServicesAccessor, ...args: unknown[]): void { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); + configurationService.updateValue('diffEditor.useInlineViewWhenSpaceIsLimited', newValue); + } +} + +registerAction2(ToggleUseInlineViewWhenSpaceIsLimited); + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: new ToggleUseInlineViewWhenSpaceIsLimited().desc.id, + title: localize('useInlineViewWhenSpaceIsLimited', "Use Inline View When Space Is Limited"), + toggled: ContextKeyExpr.has('config.diffEditor.useInlineViewWhenSpaceIsLimited'), + }, + order: 11, + group: '1_diff', + when: ContextKeyExpr.and( + EditorContextKeys.diffEditorRenderSideBySideInlineBreakpointReached, + ContextKeyEqualsExpr.create('diffEditorVersion', 2) + ) +}); + +/* +TODO@hediet add this back once move detection is more polished. +Users can still enable this via settings.json (config.diffEditor.experimental.showMoves). + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: new ToggleShowMovedCodeBlocks().desc.id, @@ -88,3 +128,30 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { group: '1_diff', when: ContextKeyEqualsExpr.create('diffEditorVersion', 2) }); +*/ + +const diffEditorCategory: ILocalizedString = { + value: localize('diffEditor', 'Diff Editor'), + original: 'Diff Editor', +}; +export class SwitchSide extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.switchSide', + title: { value: localize('switchSide', "Switch Side"), original: 'Switch Side' }, + icon: Codicon.arrowSwap, + precondition: ContextKeyExpr.and(ContextKeyEqualsExpr.create('diffEditorVersion', 2), ContextKeyExpr.has('isInDiffEditor')), + f1: true, + category: diffEditorCategory, + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget2) { + diffEditor.switchSide(); + } + } +} + +registerAction2(SwitchSide); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 34fd95848c7..e2612415c32 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -7,10 +7,7 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { findLast } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; -import { IObservable, autorun, derived, keepAlive, observableValue } from 'vs/base/common/observable'; -import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; -import { disposableObservableValue, transaction } from 'vs/base/common/observableImpl/base'; -import { derivedWithStore } from 'vs/base/common/observableImpl/derived'; +import { IObservable, autorun, autorunWithStore, derived, derivedWithStore, disposableObservableValue, keepAlive, observableValue, transaction } from 'vs/base/common/observable'; import 'vs/css!./style'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IMouseTargetViewZone } from 'vs/editor/browser/editorBrowser'; @@ -18,26 +15,31 @@ import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget'; +import { AccessibleDiffViewer } from 'vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer'; import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations'; import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorSash'; -import { DiffReview2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffReview'; import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment'; import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines'; import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart'; import { UnchangedRangesFeature } from 'vs/editor/browser/widget/diffEditorWidget2/unchangedRanges'; -import { ObservableElementSizeObserver, applyStyle, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { CSSStyle, ObservableElementSizeObserver, applyStyle, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IDimension } from 'vs/editor/common/core/dimension'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { LengthObj } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import './colors'; import { DelegatingEditor } from './delegatingEditorImpl'; import { DiffEditorEditors } from './diffEditorEditors'; import { DiffEditorOptions } from './diffEditorOptions'; @@ -48,6 +50,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { h('div.noModificationsOverlay@overlay', { style: { position: 'absolute', height: '100%', visibility: 'hidden', } }, [$('span', {}, 'No Changes')]), h('div.editor.original@original', { style: { position: 'absolute', height: '100%' } }), h('div.editor.modified@modified', { style: { position: 'absolute', height: '100%' } }), + h('div.accessibleDiffViewer@accessibleDiffViewer', { style: { position: 'absolute', height: '100%' } }), ]); private readonly _diffModel = this._register(disposableObservableValue('diffModel', undefined)); public readonly onDidChangeModel = Event.fromObservableLight(this._diffModel); @@ -65,7 +68,13 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private unchangedRangesFeature!: UnchangedRangesFeature; - private readonly _reviewPane: DiffReview2; + private _accessibleDiffViewerShouldBeVisible = observableValue('accessibleDiffViewerShouldBeVisible', false); + private _accessibleDiffViewerVisible = derived(reader => + /** @description accessibleDiffViewerVisible */ this._options.onlyShowAccessibleDiffViewer.read(reader) + ? true + : this._accessibleDiffViewerShouldBeVisible.read(reader) + ); + private _accessibleDiffViewer!: AccessibleDiffViewer; private readonly _options: DiffEditorOptions; private readonly _editors: DiffEditorEditors; @@ -76,6 +85,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { @IContextKeyService private readonly _parentContextKeyService: IContextKeyService, @IInstantiationService private readonly _parentInstantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, + @IAudioCueService private readonly _audioCueService: IAudioCueService, ) { super(); codeEditorService.willCreateDiffEditor(); @@ -83,26 +93,33 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._contextKeyService.createKey('isInDiffEditor', true); this._contextKeyService.createKey('diffEditorVersion', 2); - this._options = new DiffEditorOptions(options); - - this._contextKeyService.createKey(EditorContextKeys.isEmbeddedDiffEditor.key, false); - const isEmbeddedDiffEditorKey = EditorContextKeys.isEmbeddedDiffEditor.bindTo(this._contextKeyService); - this._register(autorun('update isEmbeddedDiffEditorKey', reader => { - isEmbeddedDiffEditorKey.set(this._options.isInEmbeddedEditor.read(reader)); - })); - this._domElement.appendChild(this.elements.root); this._rootSizeObserver = this._register(new ObservableElementSizeObserver(this.elements.root, options.dimension)); this._rootSizeObserver.setAutomaticLayout(options.automaticLayout ?? false); + this._options = new DiffEditorOptions(options, this._rootSizeObserver.width); + + this._contextKeyService.createKey(EditorContextKeys.isEmbeddedDiffEditor.key, false); + const isEmbeddedDiffEditorKey = EditorContextKeys.isEmbeddedDiffEditor.bindTo(this._contextKeyService); + this._register(autorun(reader => { + /** @description update isEmbeddedDiffEditorKey */ + isEmbeddedDiffEditorKey.set(this._options.isInEmbeddedEditor.read(reader)); + })); + + const diffEditorRenderSideBySideInlineBreakpointReachedContextKeyValue = EditorContextKeys.diffEditorRenderSideBySideInlineBreakpointReached.bindTo(this._contextKeyService); + this._register(autorun(reader => { + /** @description update accessibleDiffViewerVisible context key */ + diffEditorRenderSideBySideInlineBreakpointReachedContextKeyValue.set(this._options.couldShowInlineViewBecauseOfSize.read(reader)); + })); + this._editors = this._register(this._instantiationService.createInstance( DiffEditorEditors, this.elements.original, this.elements.modified, this._options, codeEditorWidgetOptions, - (i, c, o, o2) => this._createInnerEditor(i, c, o, o2), + (i, c, o, o2) => this._createInnerEditor(i, c, o, o2) )); this._sash = derivedWithStore('sash', (reader, store) => { @@ -117,7 +134,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { width: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)), } )); - store.add(autorun('setBoundarySashes', reader => { + store.add(autorun(reader => { + /** @description setBoundarySashes */ const boundarySashes = this._boundarySashes.read(reader); if (boundarySashes) { result.setBoundarySashes(boundarySashes); @@ -127,24 +145,29 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { }); this._register(keepAlive(this._sash, true)); - this._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => { + this._register(autorunWithStore((reader, store) => { + /** @description UnchangedRangesFeature */ this.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options)); })); - this._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => { + this._register(autorunWithStore((reader, store) => { + /** @description DiffEditorDecorations */ store.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options)); })); + this._register(autorunWithStore((reader, store) => { + /** @description ViewZoneManager */ + store.add(this._instantiationService.createInstance( + readHotReloadableExport(ViewZoneManager, reader), + this._editors, + this._diffModel, + this._options, + this, + () => this.unchangedRangesFeature.isUpdatingViewZones, + )); + })); - this._register(this._instantiationService.createInstance( - ViewZoneManager, - this._editors, - this._diffModel, - this._options, - this, - () => this.unchangedRangesFeature.isUpdatingViewZones, - )); - - this._register(autorunWithStore2('OverviewRulerPart', (reader, store) => { + this._register(autorunWithStore((reader, store) => { + /** @description OverviewRulerPart */ store.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors, this.elements.root, this._diffModel, @@ -155,10 +178,23 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { )); })); - this._reviewPane = this._register(this._instantiationService.createInstance(DiffReview2, this)); - this.elements.root.appendChild(this._reviewPane.domNode.domNode); - this.elements.root.appendChild(this._reviewPane.shadow.domNode); - this.elements.root.appendChild(this._reviewPane.actionBarContainer.domNode); + this._register(autorunWithStore((reader, store) => { + /** @description _accessibleDiffViewer */ + this._accessibleDiffViewer = store.add(this._register(this._instantiationService.createInstance( + readHotReloadableExport(AccessibleDiffViewer, reader), + this.elements.accessibleDiffViewer, + this._accessibleDiffViewerVisible, + (visible, tx) => this._accessibleDiffViewerShouldBeVisible.set(visible, tx), + this._options.onlyShowAccessibleDiffViewer.map(v => !v), + this._rootSizeObserver.width, + this._rootSizeObserver.height, + this._diffModel.map((m, r) => m?.diff.read(r)?.mappings.map(m => m.lineRangeMapping)), + this._editors, + ))); + })); + const visibility = this._accessibleDiffViewerVisible.map(v => v ? 'hidden' : 'visible'); + this._register(applyStyle(this.elements.modified, { visibility })); + this._register(applyStyle(this.elements.original, { visibility })); this._createDiffEditorContributions(); @@ -176,22 +212,21 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(applyStyle(this.elements.overlay, { width: this._layoutInfo.map((i, r) => i.originalEditor.width + (this._options.renderSideBySide.read(r) ? 0 : i.modifiedEditor.width)), - visibility: derived('visibility', reader => - (this._options.collapseUnchangedRegions.read(reader) && this._diffModel.read(reader)?.diff.read(reader)?.mappings.length === 0) - ? 'visible' : 'hidden' + visibility: derived(reader => /** @description visibility */(this._options.collapseUnchangedRegions.read(reader) && this._diffModel.read(reader)?.diff.read(reader)?.mappings.length === 0) + ? 'visible' : 'hidden' ), })); this._register(this._editors.original.onDidChangeCursorPosition(e => { const m = this._diffModel.get(); if (!m) { return; } - const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.originalRange.contains(e.position.lineNumber)); + const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.original.contains(e.position.lineNumber)); m.syncedMovedTexts.set(movedText, undefined); })); this._register(this._editors.modified.onDidChangeCursorPosition(e => { const m = this._diffModel.get(); if (!m) { return; } - const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modifiedRange.contains(e.position.lineNumber)); + const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modified.contains(e.position.lineNumber)); m.syncedMovedTexts.set(movedText, undefined); })); @@ -215,6 +250,23 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { event.event.stopPropagation(); } })); + + this._register(Event.runAndSubscribe(this._editors.modified.onDidChangeCursorPosition, (e) => { + if (e?.reason === CursorChangeReason.Explicit) { + const diff = this._diffModel.get()?.diff.get()?.mappings.find(m => m.lineRangeMapping.modifiedRange.contains(e.position.lineNumber)); + if (diff?.lineRangeMapping.modifiedRange.isEmpty) { + this._audioCueService.playAudioCue(AudioCue.diffLineDeleted); + } else if (diff?.lineRangeMapping.originalRange.isEmpty) { + this._audioCueService.playAudioCue(AudioCue.diffLineInserted); + } else if (diff) { + this._audioCueService.playAudioCue(AudioCue.diffLineModified); + } + } + })); + } + + public getContentHeight() { + return this._editors.modified.getContentHeight(); } protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { @@ -222,26 +274,23 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { return editor; } - private readonly _layoutInfo = derived('modifiedEditorLayoutInfo', (reader) => { + private readonly _layoutInfo = derived(reader => { + /** @description modifiedEditorLayoutInfo */ const width = this._rootSizeObserver.width.read(reader); const height = this._rootSizeObserver.height.read(reader); const sashLeft = this._sash.read(reader)?.sashLeft.read(reader); const originalWidth = sashLeft ?? Math.max(5, this._editors.original.getLayoutInfo().decorationsLeft); + const modifiedWidth = width - originalWidth - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0); this.elements.original.style.width = originalWidth + 'px'; this.elements.original.style.left = '0px'; - this.elements.modified.style.width = (width - originalWidth) + 'px'; + this.elements.modified.style.width = modifiedWidth + 'px'; this.elements.modified.style.left = originalWidth + 'px'; - this._editors.original.layout({ width: originalWidth, height: height }); - this._editors.modified.layout({ - width: width - originalWidth - - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0), - height - }); - this._reviewPane.layout(0, width, height); + this._editors.original.layout({ width: originalWidth, height }); + this._editors.modified.layout({ width: modifiedWidth, height }); return { modifiedEditor: this._editors.modified.getLayoutInfo(), @@ -312,6 +361,11 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { override getModel(): IDiffEditorModel | null { return this._diffModel.get()?.model ?? null; } override setModel(model: IDiffEditorModel | null | IDiffEditorViewModel): void { + if (!model && this._diffModel.get()) { + // Transitioning from a model to no-model + this._accessibleDiffViewer.close(); + } + const vm = model ? ('model' in model) ? model : this.createViewModel(model) : undefined; this._editors.original.setModel(vm ? vm.model.original : null); this._editors.modified.setModel(vm ? vm.model.modified : null); @@ -404,6 +458,14 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { diff = findLast(diffs, d => d.lineRangeMapping.modifiedRange.startLineNumber < curLineNumber) ?? diffs[diffs.length - 1]; } this._goTo(diff); + + if (diff.lineRangeMapping.modifiedRange.isEmpty) { + this._audioCueService.playAudioCue(AudioCue.diffLineDeleted); + } else if (diff.lineRangeMapping.originalRange.isEmpty) { + this._audioCueService.playAudioCue(AudioCue.diffLineInserted); + } else if (diff) { + this._audioCueService.playAudioCue(AudioCue.diffLineModified); + } } revealFirstDiff(): void { @@ -421,15 +483,80 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { }); } - diffReviewNext(): void { this._reviewPane.next(); } + accessibleDiffViewerNext(): void { this._accessibleDiffViewer.next(); } - diffReviewPrev(): void { this._reviewPane.prev(); } + accessibleDiffViewerPrev(): void { this._accessibleDiffViewer.prev(); } async waitForDiff(): Promise { const diffModel = this._diffModel.get(); if (!diffModel) { return; } await diffModel.waitForDiff(); } + + switchSide(): void { + const isModifiedFocus = this._editors.modified.hasWidgetFocus(); + const source = isModifiedFocus ? this._editors.modified : this._editors.original; + const destination = isModifiedFocus ? this._editors.original : this._editors.modified; + + const sourceSelection = source.getSelection(); + if (sourceSelection) { + const mappings = this._diffModel.get()?.diff.get()?.mappings.map(m => isModifiedFocus ? m.lineRangeMapping.flip() : m.lineRangeMapping); + if (mappings) { + const newRange1 = translatePosition(sourceSelection.getStartPosition(), mappings); + const newRange2 = translatePosition(sourceSelection.getEndPosition(), mappings); + const range = Range.plusRange(newRange1, newRange2); + destination.setSelection(range); + } + } + destination.focus(); + } +} + +function translatePosition(posInOriginal: Position, mappings: LineRangeMapping[]): Range { + const mapping = findLast(mappings, m => m.originalRange.startLineNumber <= posInOriginal.lineNumber); + if (!mapping) { + // No changes before the position + return Range.fromPositions(posInOriginal); + } + + if (mapping.originalRange.endLineNumberExclusive <= posInOriginal.lineNumber) { + const newLineNumber = posInOriginal.lineNumber - mapping.originalRange.endLineNumberExclusive + mapping.modifiedRange.endLineNumberExclusive; + return Range.fromPositions(new Position(newLineNumber, posInOriginal.column)); + } + + if (!mapping.innerChanges) { + // Only for legacy algorithm + return Range.fromPositions(new Position(mapping.modifiedRange.startLineNumber, 1)); + } + + const innerMapping = findLast(mapping.innerChanges, m => m.originalRange.getStartPosition().isBeforeOrEqual(posInOriginal)); + if (!innerMapping) { + const newLineNumber = posInOriginal.lineNumber - mapping.originalRange.startLineNumber + mapping.modifiedRange.startLineNumber; + return Range.fromPositions(new Position(newLineNumber, posInOriginal.column)); + } + + if (innerMapping.originalRange.containsPosition(posInOriginal)) { + return innerMapping.modifiedRange; + } else { + const l = lengthBetweenPositions(innerMapping.originalRange.getEndPosition(), posInOriginal); + return Range.fromPositions(addLength(innerMapping.modifiedRange.getEndPosition(), l)); + } +} + +function lengthBetweenPositions(position1: Position, position2: Position): LengthObj { + if (position1.lineNumber === position2.lineNumber) { + return new LengthObj(0, position2.column - position1.column); + } else { + return new LengthObj(position2.lineNumber - position1.lineNumber, position2.column - 1); + } +} + +function addLength(position: Position, length: LengthObj): Position { + if (length.lineCount === 0) { + return new Position(position.lineNumber, position.column + length.columnCount); + } else { + return new Position(position.lineNumber + length.lineCount, length.columnCount + 1); + } } function toLineChanges(state: DiffState): ILineChange[] { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts deleted file mode 100644 index 94d3a9cda2f..00000000000 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts +++ /dev/null @@ -1,830 +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 dom from 'vs/base/browser/dom'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { Action } from 'vs/base/common/actions'; -import { Codicon } from 'vs/base/common/codicons'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { Constants } from 'vs/base/common/uint'; -import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; -import { DiffReview } from 'vs/editor/browser/widget/diffReview'; -import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Position } from 'vs/editor/common/core/position'; -import { ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; -import { ScrollType } from 'vs/editor/common/editorCommon'; -import { ILanguageIdCodec } from 'vs/editor/common/languages'; -import { ILanguageService } from 'vs/editor/common/languages/language'; -import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; -import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { ViewLineRenderingData } from 'vs/editor/common/viewModel'; -import * as nls from 'vs/nls'; -import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; - -const DIFF_LINES_PADDING = 3; - -const enum DiffEntryType { - Equal = 0, - Insert = 1, - Delete = 2 -} - -class DiffEntry { - readonly originalLineStart: number; - readonly originalLineEnd: number; - readonly modifiedLineStart: number; - readonly modifiedLineEnd: number; - - constructor(originalLineStart: number, originalLineEnd: number, modifiedLineStart: number, modifiedLineEnd: number) { - this.originalLineStart = originalLineStart; - this.originalLineEnd = originalLineEnd; - this.modifiedLineStart = modifiedLineStart; - this.modifiedLineEnd = modifiedLineEnd; - } - - public getType(): DiffEntryType { - if (this.originalLineStart === 0) { - return DiffEntryType.Insert; - } - if (this.modifiedLineStart === 0) { - return DiffEntryType.Delete; - } - return DiffEntryType.Equal; - } -} - -const enum DiffEditorLineClasses { - Insert = 'line-insert', - Delete = 'line-delete' -} - -class Diff { - readonly entries: DiffEntry[]; - - constructor(entries: DiffEntry[]) { - this.entries = entries; - } -} - -const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add, nls.localize('diffReviewInsertIcon', 'Icon for \'Insert\' in diff review.')); -const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, nls.localize('diffReviewRemoveIcon', 'Icon for \'Remove\' in diff review.')); -const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close, nls.localize('diffReviewCloseIcon', 'Icon for \'Close\' in diff review.')); - -export class DiffReview2 extends Disposable { - - private static _ttPolicy = DiffReview._ttPolicy; // TODO inline once DiffReview is deprecated. - - private readonly _diffEditor: DiffEditorWidget2; - private _isVisible: boolean; - public readonly shadow: FastDomNode; - private readonly _actionBar: ActionBar; - public readonly actionBarContainer: FastDomNode; - public readonly domNode: FastDomNode; - private readonly _content: FastDomNode; - private readonly scrollbar: DomScrollableElement; - private _diffs: Diff[]; - private _currentDiff: Diff | null; - - constructor( - diffEditor: DiffEditorWidget2, - @ILanguageService private readonly _languageService: ILanguageService, - @IAudioCueService private readonly _audioCueService: IAudioCueService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - super(); - this._diffEditor = diffEditor; - this._isVisible = false; - - this.shadow = createFastDomNode(document.createElement('div')); - this.shadow.setClassName('diff-review-shadow'); - - this.actionBarContainer = createFastDomNode(document.createElement('div')); - this.actionBarContainer.setClassName('diff-review-actions'); - this._actionBar = this._register(new ActionBar( - this.actionBarContainer.domNode - )); - - this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + ThemeIcon.asClassName(diffReviewCloseIcon), true, async () => this.hide()), { label: false, icon: true }); - - this.domNode = createFastDomNode(document.createElement('div')); - this.domNode.setClassName('diff-review monaco-editor-background'); - - this._content = createFastDomNode(document.createElement('div')); - this._content.setClassName('diff-review-content'); - this._content.setAttribute('role', 'code'); - this.scrollbar = this._register(new DomScrollableElement(this._content.domNode, {})); - this.domNode.domNode.appendChild(this.scrollbar.getDomNode()); - - this._register(diffEditor.onDidUpdateDiff(() => { - if (!this._isVisible) { - return; - } - this._diffs = this._compute(); - this._render(); - })); - this._register(diffEditor.getModifiedEditor().onDidChangeCursorPosition(() => { - if (!this._isVisible) { - return; - } - this._render(); - })); - this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'click', (e) => { - e.preventDefault(); - - const row = dom.findParentWithClass(e.target, 'diff-review-row'); - if (row) { - this._goToRow(row); - } - })); - this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'keydown', (e) => { - if ( - e.equals(KeyCode.DownArrow) - || e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow) - || e.equals(KeyMod.Alt | KeyCode.DownArrow) - ) { - e.preventDefault(); - this._goToRow(this._getNextRow(), 'next'); - } - - if ( - e.equals(KeyCode.UpArrow) - || e.equals(KeyMod.CtrlCmd | KeyCode.UpArrow) - || e.equals(KeyMod.Alt | KeyCode.UpArrow) - ) { - e.preventDefault(); - this._goToRow(this._getPrevRow(), 'previous'); - } - - if ( - e.equals(KeyCode.Escape) - || e.equals(KeyMod.CtrlCmd | KeyCode.Escape) - || e.equals(KeyMod.Alt | KeyCode.Escape) - || e.equals(KeyMod.Shift | KeyCode.Escape) - || e.equals(KeyCode.Space) - || e.equals(KeyCode.Enter) - ) { - e.preventDefault(); - this.accept(); - } - })); - this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('accessibility.verbosity.diffEditor')) { - this._diffEditor.updateOptions({ accessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.diffEditor') }); - } - })); - this._diffs = []; - this._currentDiff = null; - } - - public prev(): void { - let index = 0; - - if (!this._isVisible) { - this._diffs = this._compute(); - } - - if (this._isVisible) { - let currentIndex = -1; - for (let i = 0, len = this._diffs.length; i < len; i++) { - if (this._diffs[i] === this._currentDiff) { - currentIndex = i; - break; - } - } - index = (this._diffs.length + currentIndex - 1); - } else { - index = this._findDiffIndex(this._diffEditor.getPosition()!); - } - - if (this._diffs.length === 0) { - // Nothing to do - return; - } - - index = index % this._diffs.length; - const entries = this._diffs[index].entries; - this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); - this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); - this._isVisible = true; - this.layout(); - this._render(); - this._goToRow(this._getPrevRow(), 'previous'); - } - - public next(): void { - let index = 0; - - if (!this._isVisible) { - this._diffs = this._compute(); - } - - if (this._isVisible) { - let currentIndex = -1; - for (let i = 0, len = this._diffs.length; i < len; i++) { - if (this._diffs[i] === this._currentDiff) { - currentIndex = i; - break; - } - } - index = (currentIndex + 1); - } else { - index = this._findDiffIndex(this._diffEditor.getPosition()!); - } - - if (this._diffs.length === 0) { - // Nothing to do - return; - } - - index = index % this._diffs.length; - const entries = this._diffs[index].entries; - this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); - this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); - this._isVisible = true; - this.layout(); - this._render(); - this._goToRow(this._getNextRow(), 'next'); - } - - private accept(): void { - let jumpToLineNumber = -1; - const current = this._getCurrentFocusedRow(); - if (current) { - const lineNumber = parseInt(current.getAttribute('data-line')!, 10); - if (!isNaN(lineNumber)) { - jumpToLineNumber = lineNumber; - } - } - this.hide(); - - if (jumpToLineNumber !== -1) { - this._diffEditor.setPosition(new Position(jumpToLineNumber, 1)); - this._diffEditor.revealPosition(new Position(jumpToLineNumber, 1), ScrollType.Immediate); - } - } - - private hide(): void { - this._isVisible = false; - this._diffEditor.updateOptions({ readOnly: false }); - this._diffEditor.focus(); - this.layout(); - this._render(); - } - - private _getPrevRow(): HTMLElement { - const current = this._getCurrentFocusedRow(); - if (!current) { - return this._getFirstRow(); - } - if (current.previousElementSibling) { - return current.previousElementSibling; - } - return current; - } - - private _getNextRow(): HTMLElement { - const current = this._getCurrentFocusedRow(); - if (!current) { - return this._getFirstRow(); - } - if (current.nextElementSibling) { - return current.nextElementSibling; - } - return current; - } - - private _getFirstRow(): HTMLElement { - return this.domNode.domNode.querySelector('.diff-review-row'); - } - - private _getCurrentFocusedRow(): HTMLElement | null { - const result = document.activeElement; - if (result && /diff-review-row/.test(result.className)) { - return result; - } - return null; - } - - private _goToRow(row: HTMLElement, type?: 'next' | 'previous'): void { - const current = this._getCurrentFocusedRow(); - row.tabIndex = 0; - row.focus(); - if (current && current !== row) { - current.tabIndex = -1; - } - const element = !type ? current : type === 'next' ? current?.nextElementSibling : current?.previousElementSibling; - if (element?.classList.contains(DiffEditorLineClasses.Insert)) { - this._audioCueService.playAudioCue(AudioCue.diffLineInserted, true); - } else if (element?.classList.contains(DiffEditorLineClasses.Delete)) { - this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, true); - } - this.scrollbar.scanDomNode(); - } - - public isVisible(): boolean { - return this._isVisible; - } - - private _width: number = 0; - private _top: number = 0; - private _height: number = 0; - - public layout(top: number = this._top, width: number = this._width, height: number = this._height): void { - this._width = width; - this._top = top; - this._height = height; - - this.shadow.setTop(top - 6); - this.shadow.setWidth(width); - this.shadow.setHeight(this._isVisible ? 6 : 0); - this.domNode.setTop(top); - this.domNode.setWidth(width); - this.domNode.setHeight(height); - this._content.setHeight(height); - this._content.setWidth(width); - - if (this._isVisible) { - this.domNode.setDisplay('block'); - this.actionBarContainer.setAttribute('aria-hidden', 'false'); - this.actionBarContainer.setDisplay('block'); - } else { - this.domNode.setDisplay('none'); - this.actionBarContainer.setAttribute('aria-hidden', 'true'); - this.actionBarContainer.setDisplay('none'); - } - } - - private _compute(): Diff[] { - const lineChanges = this._diffEditor.getLineChanges(); - if (!lineChanges || lineChanges.length === 0) { - return []; - } - const originalModel = this._diffEditor.getOriginalEditor().getModel(); - const modifiedModel = this._diffEditor.getModifiedEditor().getModel(); - - if (!originalModel || !modifiedModel) { - return []; - } - - return DiffReview2._mergeAdjacent(lineChanges, originalModel.getLineCount(), modifiedModel.getLineCount()); - } - - private static _mergeAdjacent(lineChanges: ILineChange[], originalLineCount: number, modifiedLineCount: number): Diff[] { - if (!lineChanges || lineChanges.length === 0) { - return []; - } - - const diffs: Diff[] = []; - let diffsLength = 0; - - for (let i = 0, len = lineChanges.length; i < len; i++) { - const lineChange = lineChanges[i]; - - const originalStart = lineChange.originalStartLineNumber; - const originalEnd = lineChange.originalEndLineNumber; - const modifiedStart = lineChange.modifiedStartLineNumber; - const modifiedEnd = lineChange.modifiedEndLineNumber; - - const r: DiffEntry[] = []; - let rLength = 0; - - // Emit before anchors - { - const originalEqualAbove = (originalEnd === 0 ? originalStart : originalStart - 1); - const modifiedEqualAbove = (modifiedEnd === 0 ? modifiedStart : modifiedStart - 1); - - // Make sure we don't step into the previous diff - let minOriginal = 1; - let minModified = 1; - if (i > 0) { - const prevLineChange = lineChanges[i - 1]; - - if (prevLineChange.originalEndLineNumber === 0) { - minOriginal = prevLineChange.originalStartLineNumber + 1; - } else { - minOriginal = prevLineChange.originalEndLineNumber + 1; - } - - if (prevLineChange.modifiedEndLineNumber === 0) { - minModified = prevLineChange.modifiedStartLineNumber + 1; - } else { - minModified = prevLineChange.modifiedEndLineNumber + 1; - } - } - - let fromOriginal = originalEqualAbove - DIFF_LINES_PADDING + 1; - let fromModified = modifiedEqualAbove - DIFF_LINES_PADDING + 1; - if (fromOriginal < minOriginal) { - const delta = minOriginal - fromOriginal; - fromOriginal = fromOriginal + delta; - fromModified = fromModified + delta; - } - if (fromModified < minModified) { - const delta = minModified - fromModified; - fromOriginal = fromOriginal + delta; - fromModified = fromModified + delta; - } - - r[rLength++] = new DiffEntry( - fromOriginal, originalEqualAbove, - fromModified, modifiedEqualAbove - ); - } - - // Emit deleted lines - { - if (originalEnd !== 0) { - r[rLength++] = new DiffEntry(originalStart, originalEnd, 0, 0); - } - } - - // Emit inserted lines - { - if (modifiedEnd !== 0) { - r[rLength++] = new DiffEntry(0, 0, modifiedStart, modifiedEnd); - } - } - - // Emit after anchors - { - const originalEqualBelow = (originalEnd === 0 ? originalStart + 1 : originalEnd + 1); - const modifiedEqualBelow = (modifiedEnd === 0 ? modifiedStart + 1 : modifiedEnd + 1); - - // Make sure we don't step into the next diff - let maxOriginal = originalLineCount; - let maxModified = modifiedLineCount; - if (i + 1 < len) { - const nextLineChange = lineChanges[i + 1]; - - if (nextLineChange.originalEndLineNumber === 0) { - maxOriginal = nextLineChange.originalStartLineNumber; - } else { - maxOriginal = nextLineChange.originalStartLineNumber - 1; - } - - if (nextLineChange.modifiedEndLineNumber === 0) { - maxModified = nextLineChange.modifiedStartLineNumber; - } else { - maxModified = nextLineChange.modifiedStartLineNumber - 1; - } - } - - let toOriginal = originalEqualBelow + DIFF_LINES_PADDING - 1; - let toModified = modifiedEqualBelow + DIFF_LINES_PADDING - 1; - - if (toOriginal > maxOriginal) { - const delta = maxOriginal - toOriginal; - toOriginal = toOriginal + delta; - toModified = toModified + delta; - } - if (toModified > maxModified) { - const delta = maxModified - toModified; - toOriginal = toOriginal + delta; - toModified = toModified + delta; - } - - r[rLength++] = new DiffEntry( - originalEqualBelow, toOriginal, - modifiedEqualBelow, toModified, - ); - } - - diffs[diffsLength++] = new Diff(r); - } - - // Merge adjacent diffs - let curr: DiffEntry[] = diffs[0].entries; - const r: Diff[] = []; - let rLength = 0; - for (let i = 1, len = diffs.length; i < len; i++) { - const thisDiff = diffs[i].entries; - - const currLast = curr[curr.length - 1]; - const thisFirst = thisDiff[0]; - - if ( - currLast.getType() === DiffEntryType.Equal - && thisFirst.getType() === DiffEntryType.Equal - && thisFirst.originalLineStart <= currLast.originalLineEnd - ) { - // We are dealing with equal lines that overlap - - curr[curr.length - 1] = new DiffEntry( - currLast.originalLineStart, thisFirst.originalLineEnd, - currLast.modifiedLineStart, thisFirst.modifiedLineEnd - ); - curr = curr.concat(thisDiff.slice(1)); - continue; - } - - r[rLength++] = new Diff(curr); - curr = thisDiff; - } - r[rLength++] = new Diff(curr); - return r; - } - - private _findDiffIndex(pos: Position): number { - const lineNumber = pos.lineNumber; - for (let i = 0, len = this._diffs.length; i < len; i++) { - const diff = this._diffs[i].entries; - const lastModifiedLine = diff[diff.length - 1].modifiedLineEnd; - if (lineNumber <= lastModifiedLine) { - return i; - } - } - return 0; - } - - private _render(): void { - - const originalOptions = this._diffEditor.getOriginalEditor().getOptions(); - const modifiedOptions = this._diffEditor.getModifiedEditor().getOptions(); - - const originalModel = this._diffEditor.getOriginalEditor().getModel(); - const modifiedModel = this._diffEditor.getModifiedEditor().getModel(); - - const originalModelOpts = originalModel!.getOptions(); - const modifiedModelOpts = modifiedModel!.getOptions(); - - if (!this._isVisible || !originalModel || !modifiedModel) { - dom.clearNode(this._content.domNode); - this._currentDiff = null; - this.scrollbar.scanDomNode(); - return; - } - - this._diffEditor.updateOptions({ readOnly: true }); - const diffIndex = this._findDiffIndex(this._diffEditor.getPosition()!); - - if (this._diffs[diffIndex] === this._currentDiff) { - return; - } - this._currentDiff = this._diffs[diffIndex]; - - const diffs = this._diffs[diffIndex].entries; - const container = document.createElement('div'); - container.className = 'diff-review-table'; - container.setAttribute('role', 'list'); - container.setAttribute('aria-label', 'Difference review. Use "Stage | Unstage | Revert Selected Ranges" commands'); - applyFontInfo(container, modifiedOptions.get(EditorOption.fontInfo)); - - let minOriginalLine = 0; - let maxOriginalLine = 0; - let minModifiedLine = 0; - let maxModifiedLine = 0; - for (let i = 0, len = diffs.length; i < len; i++) { - const diffEntry = diffs[i]; - const originalLineStart = diffEntry.originalLineStart; - const originalLineEnd = diffEntry.originalLineEnd; - const modifiedLineStart = diffEntry.modifiedLineStart; - const modifiedLineEnd = diffEntry.modifiedLineEnd; - - if (originalLineStart !== 0 && ((minOriginalLine === 0 || originalLineStart < minOriginalLine))) { - minOriginalLine = originalLineStart; - } - if (originalLineEnd !== 0 && ((maxOriginalLine === 0 || originalLineEnd > maxOriginalLine))) { - maxOriginalLine = originalLineEnd; - } - if (modifiedLineStart !== 0 && ((minModifiedLine === 0 || modifiedLineStart < minModifiedLine))) { - minModifiedLine = modifiedLineStart; - } - if (modifiedLineEnd !== 0 && ((maxModifiedLine === 0 || modifiedLineEnd > maxModifiedLine))) { - maxModifiedLine = modifiedLineEnd; - } - } - - const header = document.createElement('div'); - header.className = 'diff-review-row'; - - const cell = document.createElement('div'); - cell.className = 'diff-review-cell diff-review-summary'; - const originalChangedLinesCnt = maxOriginalLine - minOriginalLine + 1; - const modifiedChangedLinesCnt = maxModifiedLine - minModifiedLine + 1; - cell.appendChild(document.createTextNode(`${diffIndex + 1}/${this._diffs.length}: @@ -${minOriginalLine},${originalChangedLinesCnt} +${minModifiedLine},${modifiedChangedLinesCnt} @@`)); - header.setAttribute('data-line', String(minModifiedLine)); - - const getAriaLines = (lines: number) => { - if (lines === 0) { - return nls.localize('no_lines_changed', "no lines changed"); - } else if (lines === 1) { - return nls.localize('one_line_changed', "1 line changed"); - } else { - return nls.localize('more_lines_changed', "{0} lines changed", lines); - } - }; - - const originalChangedLinesCntAria = getAriaLines(originalChangedLinesCnt); - const modifiedChangedLinesCntAria = getAriaLines(modifiedChangedLinesCnt); - header.setAttribute('aria-label', nls.localize({ - key: 'header', - comment: [ - 'This is the ARIA label for a git diff header.', - 'A git diff header looks like this: @@ -154,12 +159,39 @@.', - 'That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.', - 'Variables 0 and 1 refer to the diff index out of total number of diffs.', - 'Variables 2 and 4 will be numbers (a line number).', - 'Variables 3 and 5 will be "no lines changed", "1 line changed" or "X lines changed", localized separately.' - ] - }, "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", (diffIndex + 1), this._diffs.length, minOriginalLine, originalChangedLinesCntAria, minModifiedLine, modifiedChangedLinesCntAria)); - header.appendChild(cell); - - // @@ -504,7 +517,7 @@ - header.setAttribute('role', 'listitem'); - container.appendChild(header); - - const lineHeight = modifiedOptions.get(EditorOption.lineHeight); - let modLine = minModifiedLine; - for (let i = 0, len = diffs.length; i < len; i++) { - const diffEntry = diffs[i]; - DiffReview2._renderSection(container, diffEntry, modLine, lineHeight, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts, this._languageService.languageIdCodec); - if (diffEntry.modifiedLineStart !== 0) { - modLine = diffEntry.modifiedLineEnd; - } - } - - dom.clearNode(this._content.domNode); - this._content.domNode.appendChild(container); - this.scrollbar.scanDomNode(); - } - - private static _renderSection( - dest: HTMLElement, diffEntry: DiffEntry, modLine: number, lineHeight: number, width: number, - originalOptions: IComputedEditorOptions, originalModel: ITextModel, originalModelOpts: TextModelResolvedOptions, - modifiedOptions: IComputedEditorOptions, modifiedModel: ITextModel, modifiedModelOpts: TextModelResolvedOptions, - languageIdCodec: ILanguageIdCodec - ): void { - - const type = diffEntry.getType(); - - let rowClassName: string = 'diff-review-row'; - let lineNumbersExtraClassName: string = ''; - const spacerClassName: string = 'diff-review-spacer'; - let spacerIcon: ThemeIcon | null = null; - switch (type) { - case DiffEntryType.Insert: - rowClassName = 'diff-review-row line-insert'; - lineNumbersExtraClassName = ' char-insert'; - spacerIcon = diffReviewInsertIcon; - break; - case DiffEntryType.Delete: - rowClassName = 'diff-review-row line-delete'; - lineNumbersExtraClassName = ' char-delete'; - spacerIcon = diffReviewRemoveIcon; - break; - } - - const originalLineStart = diffEntry.originalLineStart; - const originalLineEnd = diffEntry.originalLineEnd; - const modifiedLineStart = diffEntry.modifiedLineStart; - const modifiedLineEnd = diffEntry.modifiedLineEnd; - - const cnt = Math.max( - modifiedLineEnd - modifiedLineStart, - originalLineEnd - originalLineStart - ); - - const originalLayoutInfo = originalOptions.get(EditorOption.layoutInfo); - const originalLineNumbersWidth = originalLayoutInfo.glyphMarginWidth + originalLayoutInfo.lineNumbersWidth; - - const modifiedLayoutInfo = modifiedOptions.get(EditorOption.layoutInfo); - const modifiedLineNumbersWidth = 10 + modifiedLayoutInfo.glyphMarginWidth + modifiedLayoutInfo.lineNumbersWidth; - - for (let i = 0; i <= cnt; i++) { - const originalLine = (originalLineStart === 0 ? 0 : originalLineStart + i); - const modifiedLine = (modifiedLineStart === 0 ? 0 : modifiedLineStart + i); - - const row = document.createElement('div'); - row.style.minWidth = width + 'px'; - row.className = rowClassName; - row.setAttribute('role', 'listitem'); - if (modifiedLine !== 0) { - modLine = modifiedLine; - } - row.setAttribute('data-line', String(modLine)); - - const cell = document.createElement('div'); - cell.className = 'diff-review-cell'; - cell.style.height = `${lineHeight}px`; - row.appendChild(cell); - - const originalLineNumber = document.createElement('span'); - originalLineNumber.style.width = (originalLineNumbersWidth + 'px'); - originalLineNumber.style.minWidth = (originalLineNumbersWidth + 'px'); - originalLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; - if (originalLine !== 0) { - originalLineNumber.appendChild(document.createTextNode(String(originalLine))); - } else { - originalLineNumber.innerText = '\u00a0'; - } - cell.appendChild(originalLineNumber); - - const modifiedLineNumber = document.createElement('span'); - modifiedLineNumber.style.width = (modifiedLineNumbersWidth + 'px'); - modifiedLineNumber.style.minWidth = (modifiedLineNumbersWidth + 'px'); - modifiedLineNumber.style.paddingRight = '10px'; - modifiedLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; - if (modifiedLine !== 0) { - modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine))); - } else { - modifiedLineNumber.innerText = '\u00a0'; - } - cell.appendChild(modifiedLineNumber); - - const spacer = document.createElement('span'); - spacer.className = spacerClassName; - - if (spacerIcon) { - const spacerCodicon = document.createElement('span'); - spacerCodicon.className = ThemeIcon.asClassName(spacerIcon); - spacerCodicon.innerText = '\u00a0\u00a0'; - spacer.appendChild(spacerCodicon); - } else { - spacer.innerText = '\u00a0\u00a0'; - } - cell.appendChild(spacer); - - let lineContent: string; - if (modifiedLine !== 0) { - let html: string | TrustedHTML = this._renderLine(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, modifiedLine, languageIdCodec); - if (DiffReview2._ttPolicy) { - html = DiffReview2._ttPolicy.createHTML(html as string); - } - cell.insertAdjacentHTML('beforeend', html as string); - lineContent = modifiedModel.getLineContent(modifiedLine); - } else { - let html: string | TrustedHTML = this._renderLine(originalModel, originalOptions, originalModelOpts.tabSize, originalLine, languageIdCodec); - if (DiffReview2._ttPolicy) { - html = DiffReview2._ttPolicy.createHTML(html as string); - } - cell.insertAdjacentHTML('beforeend', html as string); - lineContent = originalModel.getLineContent(originalLine); - } - - if (lineContent.length === 0) { - lineContent = nls.localize('blankLine', "blank"); - } - - let ariaLabel: string = ''; - switch (type) { - case DiffEntryType.Equal: - if (originalLine === modifiedLine) { - ariaLabel = nls.localize({ key: 'unchangedLine', comment: ['The placeholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, originalLine); - } else { - ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine); - } - break; - case DiffEntryType.Insert: - ariaLabel = nls.localize('insertLine', "+ {0} modified line {1}", lineContent, modifiedLine); - break; - case DiffEntryType.Delete: - ariaLabel = nls.localize('deleteLine', "- {0} original line {1}", lineContent, originalLine); - break; - } - row.setAttribute('aria-label', ariaLabel); - - dest.appendChild(row); - } - } - - private static _renderLine(model: ITextModel, options: IComputedEditorOptions, tabSize: number, lineNumber: number, languageIdCodec: ILanguageIdCodec): string { - const lineContent = model.getLineContent(lineNumber); - const fontInfo = options.get(EditorOption.fontInfo); - const lineTokens = LineTokens.createEmpty(lineContent, languageIdCodec); - const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, model.mightContainNonBasicASCII()); - const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL()); - const r = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), - fontInfo.canUseHalfwidthRightwardsArrow, - lineContent, - false, - isBasicASCII, - containsRTL, - 0, - lineTokens, - [], - tabSize, - 0, - fontInfo.spaceWidth, - fontInfo.middotWidth, - fontInfo.wsmiddotWidth, - options.get(EditorOption.stopRenderingLineAfter), - options.get(EditorOption.renderWhitespace), - options.get(EditorOption.renderControlCharacters), - options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, - null - )); - - return r.html; - } -} diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts b/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts index 9e879b8240d..5aca1405d41 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts @@ -94,8 +94,10 @@ export class InlineDiffDeletedCodeMargin extends Disposable { actions.push(new Action( 'diff.clipboard.copyDeletedLineContent', isDeletion - ? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", _diff.originalRange.startLineNumber + currentLineNumberOffset) - : localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", _diff.originalRange.startLineNumber + currentLineNumberOffset), + ? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", + _diff.originalRange.startLineNumber + currentLineNumberOffset) + : localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", + _diff.originalRange.startLineNumber + currentLineNumberOffset), undefined, true, async () => { @@ -111,9 +113,15 @@ export class InlineDiffDeletedCodeMargin extends Disposable { } const readOnly = _modifiedEditor.getOption(EditorOption.readOnly); if (!readOnly) { - actions.push(new Action('diff.inline.revertChange', localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => { - this._editor.revert(this._diff); - })); + actions.push(new Action( + 'diff.inline.revertChange', + localize('diff.inline.revertChange.label', "Revert this change"), + undefined, + true, + async () => { + this._editor.revert(this._diff); + }) + ); } return actions; }, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts index 8fca03ee704..9948632791e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts @@ -7,9 +7,8 @@ import { $ } from 'vs/base/browser/dom'; import { ArrayQueue } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IObservable, derived, observableFromEvent, observableValue } from 'vs/base/common/observable'; -import { autorun, autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { IObservable, autorun, autorunWithStore, derived, observableFromEvent, observableValue } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { assertIsDefined } from 'vs/base/common/types'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; @@ -68,8 +67,8 @@ export class ViewZoneManager extends Disposable { state.set(state.get() + 1, undefined); }, 0)); - this._register(this._editors.original.onDidChangeViewZones((args) => { if (!isChangingViewZones && !this._canIgnoreViewZoneUpdateEvent()) { updateImmediately.schedule(); } })); - this._register(this._editors.modified.onDidChangeViewZones((args) => { if (!isChangingViewZones && !this._canIgnoreViewZoneUpdateEvent()) { updateImmediately.schedule(); } })); + this._register(this._editors.original.onDidChangeViewZones((_args) => { if (!isChangingViewZones && !this._canIgnoreViewZoneUpdateEvent()) { updateImmediately.schedule(); } })); + this._register(this._editors.modified.onDidChangeViewZones((_args) => { if (!isChangingViewZones && !this._canIgnoreViewZoneUpdateEvent()) { updateImmediately.schedule(); } })); this._register(this._editors.original.onDidChangeConfiguration((args) => { if (args.hasChanged(EditorOption.wrappingInfo)) { updateImmediately.schedule(); } })); this._register(this._editors.modified.onDidChangeConfiguration((args) => { if (args.hasChanged(EditorOption.wrappingInfo)) { updateImmediately.schedule(); } })); @@ -80,21 +79,25 @@ export class ViewZoneManager extends Disposable { const alignmentViewZoneIdsOrig = new Set(); const alignmentViewZoneIdsMod = new Set(); - const alignments = derived('alignments', (reader) => { + const alignments = derived((reader) => { + /** @description alignments */ const diffModel = this._diffModel.read(reader); const diff = diffModel?.diff.read(reader); if (!diffModel || !diff) { return null; } state.read(reader); - return computeRangeAlignment(this._editors.original, this._editors.modified, diff.mappings, alignmentViewZoneIdsOrig, alignmentViewZoneIdsMod); + const renderSideBySide = this._options.renderSideBySide.read(reader); + const innerHunkAlignment = renderSideBySide; + return computeRangeAlignment(this._editors.original, this._editors.modified, diff.mappings, alignmentViewZoneIdsOrig, alignmentViewZoneIdsMod, innerHunkAlignment); }); - const alignmentsSyncedMovedText = derived('alignments', (reader) => { + const alignmentsSyncedMovedText = derived((reader) => { + /** @description alignments */ const syncedMovedText = this._diffModel.read(reader)?.syncedMovedTexts.read(reader); if (!syncedMovedText) { return null; } state.read(reader); const mappings = syncedMovedText.changes.map(c => new DiffMapping(c)); // TODO dont include alignments outside syncedMovedText - return computeRangeAlignment(this._editors.original, this._editors.modified, mappings, alignmentViewZoneIdsOrig, alignmentViewZoneIdsMod); + return computeRangeAlignment(this._editors.original, this._editors.modified, mappings, alignmentViewZoneIdsOrig, alignmentViewZoneIdsMod, true); }); function createFakeLinesDiv(): HTMLElement { @@ -104,7 +107,8 @@ export class ViewZoneManager extends Disposable { } const alignmentViewZonesDisposables = this._register(new DisposableStore()); - const alignmentViewZones = derived<{ orig: IViewZoneWithZoneId[]; mod: IViewZoneWithZoneId[] }>('alignment viewzones', (reader) => { + const alignmentViewZones = derived<{ orig: IViewZoneWithZoneId[]; mod: IViewZoneWithZoneId[] }>((reader) => { + /** @description alignment viewzones */ alignmentViewZonesDisposables.clear(); const alignmentsVal = alignments.read(reader) || []; @@ -243,7 +247,7 @@ export class ViewZoneManager extends Disposable { } else { const delta = a.modifiedHeightInPx - a.originalHeightInPx; if (delta > 0) { - if (syncedMovedText?.lineRangeMapping.originalRange.contains(a.originalRange.endLineNumberExclusive - 1)) { + if (syncedMovedText?.lineRangeMapping.original.contains(a.originalRange.endLineNumberExclusive - 1)) { continue; } @@ -254,7 +258,7 @@ export class ViewZoneManager extends Disposable { showInHiddenAreas: true, }); } else { - if (syncedMovedText?.lineRangeMapping.modifiedRange.contains(a.modifiedRange.endLineNumberExclusive - 1)) { + if (syncedMovedText?.lineRangeMapping.modified.contains(a.modifiedRange.endLineNumberExclusive - 1)) { continue; } @@ -281,8 +285,8 @@ export class ViewZoneManager extends Disposable { } for (const a of alignmentsSyncedMovedText.read(reader) ?? []) { - if (!syncedMovedText?.lineRangeMapping.originalRange.intersect(a.originalRange) - && !syncedMovedText?.lineRangeMapping.modifiedRange.intersect(a.modifiedRange)) { + if (!syncedMovedText?.lineRangeMapping.original.intersect(a.originalRange) + && !syncedMovedText?.lineRangeMapping.modified.intersect(a.modifiedRange)) { // ignore unrelated alignments outside the synced moved text continue; } @@ -308,7 +312,8 @@ export class ViewZoneManager extends Disposable { return { orig: origViewZones, mod: modViewZones }; }); - this._register(autorunWithStore2('alignment viewzones', (reader) => { + this._register(autorunWithStore((reader) => { + /** @description alignment viewzones */ const scrollState = StableEditorScrollState.capture(this._editors.modified); const alignmentViewZones_ = alignmentViewZones.read(reader); @@ -340,6 +345,17 @@ export class ViewZoneManager extends Disposable { scrollState.restore(this._editors.modified); })); + this._register(toDisposable(() => { + this._editors.original.changeViewZones((a) => { + for (const id of alignmentViewZoneIdsOrig) { a.removeZone(id); } + alignmentViewZoneIdsOrig.clear(); + }); + this._editors.modified.changeViewZones((a) => { + for (const id of alignmentViewZoneIdsMod) { a.removeZone(id); } + alignmentViewZoneIdsMod.clear(); + }); + })); + let ignoreChange = false; this._register(this._editors.original.onDidScrollChange(e => { if (e.scrollLeftChanged && !ignoreChange) { @@ -367,7 +383,8 @@ export class ViewZoneManager extends Disposable { // origOffset - modOffset = heightOfLines(1..Y) - heightOfLines(1..X) // origScrollTop >= 0, modScrollTop >= 0 - this._register(autorun('update scroll modified', (reader) => { + this._register(autorun(reader => { + /** @description update scroll modified */ const newScrollTopModified = this._originalScrollTop.read(reader) - (this._originalScrollOffsetAnimated.get() - this._modifiedScrollOffsetAnimated.read(reader)) - (this._originalTopPadding.get() - this._modifiedTopPadding.read(reader)); @@ -376,7 +393,8 @@ export class ViewZoneManager extends Disposable { } })); - this._register(autorun('update scroll original', (reader) => { + this._register(autorun(reader => { + /** @description update scroll original */ const newScrollTopOriginal = this._modifiedScrollTop.read(reader) - (this._modifiedScrollOffsetAnimated.get() - this._originalScrollOffsetAnimated.read(reader)) - (this._modifiedTopPadding.get() - this._originalTopPadding.read(reader)); @@ -386,13 +404,14 @@ export class ViewZoneManager extends Disposable { })); - this._register(autorun('update', reader => { + this._register(autorun(reader => { + /** @description update editor top offsets */ const m = this._diffModel.read(reader)?.syncedMovedTexts.read(reader); let deltaOrigToMod = 0; if (m) { - const trueTopOriginal = this._editors.original.getTopForLineNumber(m.lineRangeMapping.originalRange.startLineNumber, true) - this._originalTopPadding.get(); - const trueTopModified = this._editors.modified.getTopForLineNumber(m.lineRangeMapping.modifiedRange.startLineNumber, true) - this._modifiedTopPadding.get(); + const trueTopOriginal = this._editors.original.getTopForLineNumber(m.lineRangeMapping.original.startLineNumber, true) - this._originalTopPadding.get(); + const trueTopModified = this._editors.modified.getTopForLineNumber(m.lineRangeMapping.modified.startLineNumber, true) - this._modifiedTopPadding.get(); deltaOrigToMod = trueTopModified - trueTopOriginal; } @@ -444,6 +463,7 @@ function computeRangeAlignment( diffs: readonly DiffMapping[], originalEditorAlignmentViewZones: ReadonlySet, modifiedEditorAlignmentViewZones: ReadonlySet, + innerHunkAlignment: boolean, ): ILineRangeAlignment[] { const originalLineHeightOverrides = new ArrayQueue(getAdditionalLineHeights(originalEditor, originalEditorAlignmentViewZones)); const modifiedLineHeightOverrides = new ArrayQueue(getAdditionalLineHeights(modifiedEditor, modifiedEditorAlignmentViewZones)); @@ -504,20 +524,58 @@ function computeRangeAlignment( const c = m.lineRangeMapping; handleAlignmentsOutsideOfDiffs(c.originalRange.startLineNumber, c.modifiedRange.startLineNumber); - const originalAdditionalHeight = originalLineHeightOverrides - .takeWhile(v => v.lineNumber < c.originalRange.endLineNumberExclusive) - ?.reduce((p, c) => p + c.heightInPx, 0) ?? 0; - const modifiedAdditionalHeight = modifiedLineHeightOverrides - .takeWhile(v => v.lineNumber < c.modifiedRange.endLineNumberExclusive) - ?.reduce((p, c) => p + c.heightInPx, 0) ?? 0; + let first = true; + let lastModLineNumber = c.modifiedRange.startLineNumber; + let lastOrigLineNumber = c.originalRange.startLineNumber; - result.push({ - originalRange: c.originalRange, - modifiedRange: c.modifiedRange, - originalHeightInPx: c.originalRange.length * origLineHeight + originalAdditionalHeight, - modifiedHeightInPx: c.modifiedRange.length * modLineHeight + modifiedAdditionalHeight, - diff: m.lineRangeMapping, - }); + function emitAlignment(origLineNumberExclusive: number, modLineNumberExclusive: number) { + if (origLineNumberExclusive < lastOrigLineNumber || modLineNumberExclusive < lastModLineNumber) { + return; + } + if (first) { + first = false; + } else if (origLineNumberExclusive === lastOrigLineNumber || modLineNumberExclusive === lastModLineNumber) { + return; + } + const originalRange = new LineRange(lastOrigLineNumber, origLineNumberExclusive); + const modifiedRange = new LineRange(lastModLineNumber, modLineNumberExclusive); + if (originalRange.isEmpty && modifiedRange.isEmpty) { + return; + } + + const originalAdditionalHeight = originalLineHeightOverrides + .takeWhile(v => v.lineNumber < origLineNumberExclusive) + ?.reduce((p, c) => p + c.heightInPx, 0) ?? 0; + const modifiedAdditionalHeight = modifiedLineHeightOverrides + .takeWhile(v => v.lineNumber < modLineNumberExclusive) + ?.reduce((p, c) => p + c.heightInPx, 0) ?? 0; + + result.push({ + originalRange, + modifiedRange, + originalHeightInPx: originalRange.length * origLineHeight + originalAdditionalHeight, + modifiedHeightInPx: modifiedRange.length * modLineHeight + modifiedAdditionalHeight, + diff: m.lineRangeMapping, + }); + + lastOrigLineNumber = origLineNumberExclusive; + lastModLineNumber = modLineNumberExclusive; + } + + if (innerHunkAlignment) { + for (const i of c.innerChanges || []) { + if (i.originalRange.startColumn > 1 && i.modifiedRange.startColumn > 1) { + // There is some unmodified text on this line before the diff + emitAlignment(i.originalRange.startLineNumber, i.modifiedRange.startLineNumber); + } + if (i.originalRange.endColumn < originalEditor.getModel()!.getLineMaxColumn(i.originalRange.endLineNumber)) { + // // There is some unmodified text on this line after the diff + emitAlignment(i.originalRange.endLineNumber, i.modifiedRange.endLineNumber); + } + } + } + + emitAlignment(c.originalRange.endLineNumberExclusive, c.modifiedRange.endLineNumberExclusive); lastOriginalLineNumber = c.originalRange.endLineNumberExclusive; lastModifiedLineNumber = c.modifiedRange.endLineNumberExclusive; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 4c85fd8afa6..b4b628c711f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -27,7 +27,8 @@ export class MovedBlocksLinesPart extends Disposable { element.setAttribute('class', 'moved-blocks-lines'); this._rootElement.appendChild(element); - this._register(autorun('update', (reader) => { + this._register(autorun(reader => { + /** @description update moved blocks lines positioning */ const info = this._originalEditorLayoutInfo.read(reader); const info2 = this._modifiedEditorLayoutInfo.read(reader); if (!info || !info2) { @@ -43,8 +44,15 @@ export class MovedBlocksLinesPart extends Disposable { const modifiedScrollTop = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop()); const viewZonesChanged = observableSignalFromEvent('onDidChangeViewZones', this._editors.modified.onDidChangeViewZones); - this._register(autorun('update', (reader) => { + this._register(autorun(reader => { element.replaceChildren(); + + /** @description update moved blocks lines */ + const moves = this._diffModel.read(reader)?.diff.read(reader)?.movedTexts; + if (!moves) { + return; + } + viewZonesChanged.read(reader); const info = this._originalEditorLayoutInfo.read(reader); @@ -54,11 +62,6 @@ export class MovedBlocksLinesPart extends Disposable { } const width = info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding; - const moves = this._diffModel.read(reader)?.diff.read(reader)?.movedTexts; - if (!moves) { - return; - } - let idx = 0; for (const m of moves) { function computeLineStart(range: LineRange, editor: ICodeEditor) { @@ -67,9 +70,9 @@ export class MovedBlocksLinesPart extends Disposable { return (t1 + t2) / 2; } - const start = computeLineStart(m.lineRangeMapping.originalRange, this._editors.original); + const start = computeLineStart(m.lineRangeMapping.original, this._editors.original); const startOffset = originalScrollTop.read(reader); - const end = computeLineStart(m.lineRangeMapping.modifiedRange, this._editors.modified); + const end = computeLineStart(m.lineRangeMapping.modified, this._editors.modified); const endOffset = modifiedScrollTop.read(reader); const top = start - startOffset; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts index 7f43570c98c..c051508fd84 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts @@ -6,15 +6,15 @@ import { EventType, addDisposableListener, addStandardDisposableListener, h } from 'vs/base/browser/dom'; import { createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { Color } from 'vs/base/common/color'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, derived, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable'; -import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; +import { IObservable, autorun, autorunWithStore, derived, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { appendRemoveOnDispose } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; -import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; +import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager'; @@ -40,7 +40,8 @@ export class OverviewRulerPart extends Disposable { const currentColorTheme = observableFromEvent(this._themeService.onDidColorThemeChange, () => this._themeService.getColorTheme()); - const currentColors = derived('colors', reader => { + const currentColors = derived(reader => { + /** @description colors */ const theme = currentColorTheme.read(reader); const insertColor = theme.getColor(diffOverviewRulerInserted) || (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); const removeColor = theme.getColor(diffOverviewRulerRemoved) || (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); @@ -50,8 +51,8 @@ export class OverviewRulerPart extends Disposable { const scrollTopObservable = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop()); const scrollHeightObservable = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollHeight()); - // overview ruler - this._register(autorunWithStore2('create diff editor overview ruler if enabled', (reader, store) => { + this._register(autorunWithStore((reader, store) => { + /** @description create diff editor overview ruler if enabled */ if (!this._options.renderOverviewRuler.read(reader)) { return; } @@ -72,7 +73,8 @@ export class OverviewRulerPart extends Disposable { }, { passive: false })); store.add(appendRemoveOnDispose(this._rootElement, diffOverviewRoot)); - store.add(autorunWithStore2('recreate overview rules when model changes', (reader, store) => { + store.add(autorunWithStore((reader, store) => { + /** @description recreate overview rules when model changes */ const m = this._diffModel.read(reader); const originalOverviewRuler = this._editors.original.createOverviewRuler('original diffOverviewRuler'); @@ -97,7 +99,8 @@ export class OverviewRulerPart extends Disposable { const origHiddenRangesChanged = observableSignalFromEvent('hiddenRangesChanged', this._editors.original.onDidChangeHiddenAreas); const modHiddenRangesChanged = observableSignalFromEvent('hiddenRangesChanged', this._editors.modified.onDidChangeHiddenAreas); - store.add(autorun('set overview ruler zones', (reader) => { + store.add(autorun(reader => { + /** @description set overview ruler zones */ origViewZonesChanged.read(reader); modViewZonesChanged.read(reader); origHiddenRangesChanged.read(reader); @@ -125,7 +128,8 @@ export class OverviewRulerPart extends Disposable { modifiedOverviewRuler?.setZones(createZones((diff || []).map(d => d.lineRangeMapping.modifiedRange), colors.insertColor, this._editors.modified)); })); - store.add(autorun('layout overview ruler', (reader) => { + store.add(autorun(reader => { + /** @description layout overview ruler */ const height = this._rootHeight.read(reader); const width = this._rootWidth.read(reader); const layoutInfo = this._modifiedEditorLayoutInfo.read(reader); @@ -146,15 +150,18 @@ export class OverviewRulerPart extends Disposable { const scrollTop = scrollTopObservable.read(reader); const scrollHeight = scrollHeightObservable.read(reader); - const computedAvailableSize = Math.max(0, layoutInfo.height); - const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); - const computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + const scrollBarOptions = this._editors.modified.getOption(EditorOption.scrollbar); + const state = new ScrollbarState( + scrollBarOptions.verticalHasArrows ? scrollBarOptions.arrowSize : 0, + scrollBarOptions.verticalScrollbarSize, + 0, + layoutInfo.height, + scrollHeight, + scrollTop + ); - const computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); - const computedSliderPosition = Math.floor(scrollTop * computedRatio); - - viewportDomElement.setTop(computedSliderPosition); - viewportDomElement.setHeight(computedSliderSize); + viewportDomElement.setTop(state.getSliderPosition()); + viewportDomElement.setHeight(state.getSliderSize()); } else { viewportDomElement.setTop(0); viewportDomElement.setHeight(0); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts index c90137175ad..b33db4762e0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts @@ -44,26 +44,12 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati source.mightContainNonBasicASCII, source.mightContainRTL, options, - sb, - //marginDomNode + sb )); renderedLineCount++; lastBreakOffset = breakOffset; } viewLineCounts.push(lineBreakData.breakOffsets.length); - - - /* - const marginDomNode2 = document.createElement('div'); - marginDomNode2.className = 'gutter-delete'; - result.original.push({ - afterLineNumber: lineNumber, - afterColumn: 0, - heightInLines: lineBreakData.breakOffsets.length - 1, - domNode: createFakeLinesDiv(), - marginDomNode: marginDomNode2 - }); - */ } else { viewLineCounts.push(1); maxCharsPerLine = Math.max(maxCharsPerLine, renderOriginalLine( @@ -197,4 +183,3 @@ function renderOriginalLine( return output.characterMapping.getHorizontalOffset(output.characterMapping.length); } - diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css index cc28c99cdb6..f8243853b70 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ b/src/vs/editor/browser/widget/diffEditorWidget2/style.css @@ -78,12 +78,12 @@ border: 2px solid var(--vscode-diffEditor-move-border); } -.monaco-editor .moved-blocks-lines { +.monaco-diff-editor .moved-blocks-lines { position: absolute; pointer-events: none; } -.monaco-editor .moved-blocks-lines path { +.monaco-diff-editor .moved-blocks-lines path { fill: none; stroke: var(--vscode-diffEditor-move-border); stroke-width: 2; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 844dcc30f2e..7272f56b11f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -8,9 +8,7 @@ import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Codicon } from 'vs/base/common/codicons'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, observableFromEvent, transaction } from 'vs/base/common/observable'; -import { autorun, autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; -import { derived, derivedWithStore } from 'vs/base/common/observableImpl/derived'; +import { IObservable, autorun, derived, derivedWithStore, observableFromEvent, transaction } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { isDefined } from 'vs/base/common/types'; import { ICodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; @@ -75,13 +73,13 @@ export class UnchangedRangesFeature extends Disposable { } { - const d = derived('hiddenOriginalRangeStart', reader => r.getHiddenOriginalRange(reader).startLineNumber - 1); + const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, !sideBySide)); } { - const d = derived('hiddenModifiedRangeStart', reader => r.getHiddenModifiedRange(reader).startLineNumber - 1); + const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); const modViewZone = new PlaceholderViewZone(d, 24); modViewZones.push(modViewZone); store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, false)); @@ -99,12 +97,14 @@ export class UnchangedRangesFeature extends Disposable { }; const unchangedLinesDecorationShow: IModelDecorationOptions = { description: 'Fold Unchanged', - glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendMarkdown(localize('foldUnchanged', 'Fold Unchanged Region')), + glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }) + .appendMarkdown(localize('foldUnchanged', 'Fold Unchanged Region')), glyphMarginClassName: 'fold-unchanged ' + ThemeIcon.asClassName(Codicon.fold), zIndex: 10001, }; - this._register(applyObservableDecorations(this._editors.original, derived('decorations', (reader) => { + this._register(applyObservableDecorations(this._editors.original, derived(reader => { + /** @description decorations */ const curUnchangedRegions = unchangedRegions.read(reader); const result = curUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, @@ -121,7 +121,8 @@ export class UnchangedRangesFeature extends Disposable { return result; }))); - this._register(applyObservableDecorations(this._editors.modified, derived('decorations', (reader) => { + this._register(applyObservableDecorations(this._editors.modified, derived(reader => { + /** @description decorations */ const curUnchangedRegions = unchangedRegions.read(reader); const result = curUnchangedRegions.map(r => ({ range: r.modifiedRange.toInclusiveRange()!, @@ -141,7 +142,8 @@ export class UnchangedRangesFeature extends Disposable { this._register(applyViewZones(this._editors.original, viewZones.map(v => v.origViewZones), v => this._isUpdatingViewZones = v)); this._register(applyViewZones(this._editors.modified, viewZones.map(v => v.modViewZones), v => this._isUpdatingViewZones = v)); - this._register(autorunWithStore2('update folded unchanged regions', (reader, store) => { + this._register(autorun((reader) => { + /** @description update folded unchanged regions */ const curUnchangedRegions = unchangedRegions.read(reader); this._editors.original.setHiddenAreas(curUnchangedRegions.map(r => r.getHiddenOriginalRange(reader).toInclusiveRange()).filter(isDefined)); this._editors.modified.setHiddenAreas(curUnchangedRegions.map(r => r.getHiddenModifiedRange(reader).toInclusiveRange()).filter(isDefined)); @@ -177,14 +179,14 @@ export class UnchangedRangesFeature extends Disposable { class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly _nodes = h('div.diff-hidden-lines', [ - h('div.top@top', { title: 'Click or drag to show more above' }), + h('div.top@top', { title: localize('diff.hiddenLines.top', 'Click or drag to show more above') }), h('div.center@content', { style: { display: 'flex' } }, [ h('div@first', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }, - [$('a', { title: 'Show all', role: 'button', onclick: () => { this._unchangedRegion.showAll(undefined); } }, ...renderLabelWithIcons('$(unfold)'))] + [$('a', { title: localize('showAll', 'Show all'), role: 'button', onclick: () => { this._unchangedRegion.showAll(undefined); } }, ...renderLabelWithIcons('$(unfold)'))] ), h('div@others', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }), ]), - h('div.bottom@bottom', { title: 'Click or drag to show more below', role: 'button' }), + h('div.bottom@bottom', { title: localize('diff.bottom', 'Click or drag to show more below'), role: 'button' }), ]); constructor( @@ -284,12 +286,13 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { }); })); - this._register(autorun('update labels', (reader) => { + this._register(autorun(reader => { + /** @description update labels */ const children: HTMLElement[] = []; if (!this.hide && true) { const lineCount = _unchangedRegion.getHiddenModifiedRange(reader).length; - const linesHiddenText = `${lineCount} Hidden Lines`; + const linesHiddenText = localize('hiddenLines', '{0} Hidden Lines', lineCount); children.push($('span', { title: linesHiddenText }, linesHiddenText)); } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts index 7d497d55628..9a86bfd448b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts @@ -5,7 +5,7 @@ import { IDimension } from 'vs/base/browser/dom'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, IReader, ISettableObservable, autorun, autorunHandleChanges, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; +import { IObservable, IReader, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { ICodeEditor, IOverlayWidget, IViewZone } from 'vs/editor/browser/editorBrowser'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; @@ -55,7 +55,7 @@ export function joinCombine(arr1: readonly T[], arr2: readonly T[], keySelect export function applyObservableDecorations(editor: ICodeEditor, decorations: IObservable): IDisposable { const d = new DisposableStore(); const decorationsCollection = editor.createDecorationsCollection(); - d.add(autorun(`Apply decorations from ${decorations.debugName}`, reader => { + d.add(autorunOpts({ debugName: `Apply decorations from ${decorations.debugName}` }, reader => { const d = decorations.read(reader); decorationsCollection.set(d); })); @@ -102,6 +102,7 @@ export class ObservableElementSizeObserver extends Disposable { this._height = observableValue('height', this.elementSizeObserver.getHeight()); this._register(this.elementSizeObserver.onDidChange(e => transaction(tx => { + /** @description Set width/height from elementSizeObserver */ this._width.set(this.elementSizeObserver.getWidth(), tx); this._height.set(this.elementSizeObserver.getHeight(), tx); }))); @@ -130,7 +131,7 @@ export function animatedObservable(base: IObservable, store: Di const durationMs = 300; let animationFrame: number | undefined = undefined; - store.add(autorunHandleChanges('update value', { + store.add(autorunHandleChanges({ createEmptyChangeSummary: () => ({ animate: false }), handleChange: (ctx, s) => { if (ctx.didChange(base)) { @@ -139,6 +140,7 @@ export function animatedObservable(base: IObservable, store: Di return true; } }, (reader, s) => { + /** @description update value */ if (animationFrame !== undefined) { cancelAnimationFrame(animationFrame); animationFrame = undefined; @@ -269,7 +271,8 @@ export interface CSSStyle { } export function applyStyle(domNode: HTMLElement, style: Partial<{ [TKey in keyof CSSStyle]: CSSStyle[TKey] | IObservable | undefined }>) { - return autorun('applyStyle', (reader) => { + return autorun(reader => { + /** @description applyStyle */ for (let [key, val] of Object.entries(style)) { if (val && typeof val === 'object' && 'read' in val) { val = val.read(reader) as any; @@ -316,7 +319,8 @@ export function applyViewZones(editor: ICodeEditor, viewZones: IObservable { + store.add(autorun(reader => { + /** @description applyViewZones */ const curViewZones = viewZones.read(reader); const viewZonIdsPerViewZone = new Map(); @@ -335,7 +339,7 @@ export function applyViewZones(editor: ICodeEditor, viewZones: IObservable { + /** @description layoutZone on change */ for (const vz of curViewZones) { if (vz.onChange) { viewZoneIdPerOnChangeObservable.set(vz.onChange, viewZonIdsPerViewZone.get(vz)!); diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index 553470a8494..dc5dd6c299c 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -20,6 +20,8 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; export class EmbeddedCodeEditorWidget extends CodeEditorWidget { @@ -67,6 +69,9 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { } } +/** + * @deprecated Use EmbeddedDiffEditorWidget2 instead. + */ export class EmbeddedDiffEditorWidget extends DiffEditorWidget { private readonly _parentEditor: ICodeEditor; @@ -111,3 +116,47 @@ export class EmbeddedDiffEditorWidget extends DiffEditorWidget { super.updateOptions(this._overwriteOptions); } } + +/** + * TODO: Rename to EmbeddedDiffEditorWidget once EmbeddedDiffEditorWidget is removed. + */ +export class EmbeddedDiffEditorWidget2 extends DiffEditorWidget2 { + + private readonly _parentEditor: ICodeEditor; + private readonly _overwriteOptions: IDiffEditorOptions; + + constructor( + domElement: HTMLElement, + options: Readonly, + codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, + parentEditor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @IAudioCueService audioCueService: IAudioCueService, + ) { + super(domElement, parentEditor.getRawOptions(), codeEditorWidgetOptions, contextKeyService, instantiationService, codeEditorService, audioCueService); + + this._parentEditor = parentEditor; + this._overwriteOptions = options; + + // Overwrite parent's options + super.updateOptions(this._overwriteOptions); + + this._register(parentEditor.onDidChangeConfiguration(e => this._onParentConfigurationChanged(e))); + } + + getParentEditor(): ICodeEditor { + return this._parentEditor; + } + + private _onParentConfigurationChanged(e: ConfigurationChangedEvent): void { + super.updateOptions(this._parentEditor.getRawOptions()); + super.updateOptions(this._overwriteOptions); + } + + override updateOptions(newOptions: IEditorOptions): void { + objects.mixin(this._overwriteOptions, newOptions, true); + super.updateOptions(this._overwriteOptions); + } +} diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 70dd0b515db..c3ce32a7e1e 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -162,6 +162,16 @@ const editorConfiguration: IConfigurationNode = { default: true, description: nls.localize('sideBySide', "Controls whether the diff editor shows the diff side by side or inline.") }, + 'diffEditor.renderSideBySideInlineBreakpoint': { + type: 'number', + default: true, + description: nls.localize('renderSideBySideInlineBreakpoint', "If the diff editor width is smaller than this value, the inline view is used.") + }, + 'diffEditor.useInlineViewWhenSpaceIsLimited': { + type: 'boolean', + default: true, + description: nls.localize('useInlineViewWhenSpaceIsLimited', "If enabled and the editor width is too small, the inline view is used.") + }, 'diffEditor.renderMarginRevertIcon': { type: 'boolean', default: true, @@ -195,7 +205,7 @@ const editorConfiguration: IConfigurationNode = { 'diffEditor.diffAlgorithm': { type: 'string', enum: ['legacy', 'advanced'], - default: 'legacy', + default: 'advanced', markdownEnumDescriptions: [ nls.localize('diffAlgorithm.legacy', "Uses the legacy diffing algorithm."), nls.localize('diffAlgorithm.advanced', "Uses the advanced diffing algorithm."), @@ -216,6 +226,7 @@ const editorConfiguration: IConfigurationNode = { type: 'boolean', default: false, description: nls.localize('useVersion2', "Controls whether the diff editor uses the new or the old implementation."), + tags: ['experimental'], }, 'diffEditor.experimental.showEmptyDecorations': { type: 'boolean', diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index b2f09749f2f..e06cb1a2e12 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -58,6 +58,11 @@ export interface IEditorOptions { * The aria label for the editor's textarea (when it is focused). */ ariaLabel?: string; + + /** + * Whether the aria-required attribute should be set on the editors textarea. + */ + ariaRequired?: boolean; /** * Control whether a screen reader announces inline suggestion content immediately. */ @@ -750,6 +755,16 @@ export interface IDiffEditorBaseOptions { * Defaults to true. */ renderSideBySide?: boolean; + /** + * When `renderSideBySide` is enabled, `useInlineViewWhenSpaceIsLimited` is set, + * and the diff editor has a width less than `renderSideBySideInlineBreakpoint`, the inline view is used. + */ + renderSideBySideInlineBreakpoint?: number | undefined; + /** + * When `renderSideBySide` is enabled, `useInlineViewWhenSpaceIsLimited` is set, + * and the diff editor has a width less than `renderSideBySideInlineBreakpoint`, the inline view is used. + */ + useInlineViewWhenSpaceIsLimited?: boolean; /** * Timeout in milliseconds after which diff computation is cancelled. * Defaults to 5000. @@ -822,6 +837,11 @@ export interface IDiffEditorBaseOptions { * Defaults to false */ isInEmbeddedEditor?: boolean; + + /** + * If the diff editor should only show the difference review mode. + */ + onlyShowAccessibleDiffViewer?: boolean; } /** @@ -4972,6 +4992,7 @@ export const enum EditorOption { accessibilitySupport, accessibilityPageSize, ariaLabel, + ariaRequired, autoClosingBrackets, screenReaderAnnounceInlineSuggestion, autoClosingDelete, @@ -5142,6 +5163,9 @@ export const EditorOptions = { ariaLabel: register(new EditorStringOption( EditorOption.ariaLabel, 'ariaLabel', nls.localize('editorViewAccessibleLabel', "Editor content") )), + ariaRequired: register(new EditorBooleanOption( + EditorOption.ariaRequired, 'ariaRequired', false, undefined + )), screenReaderAnnounceInlineSuggestion: register(new EditorBooleanOption( EditorOption.screenReaderAnnounceInlineSuggestion, 'screenReaderAnnounceInlineSuggestion', true, { diff --git a/src/vs/editor/common/core/cursorColumns.ts b/src/vs/editor/common/core/cursorColumns.ts index 6f8cd6fbc72..669c38b43f5 100644 --- a/src/vs/editor/common/core/cursorColumns.ts +++ b/src/vs/editor/common/core/cursorColumns.ts @@ -116,7 +116,7 @@ export class CursorColumns { } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) * @see {@link CursorColumns} */ public static nextRenderTabStop(visibleColumn: number, tabSize: number): number { @@ -124,7 +124,7 @@ export class CursorColumns { } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) * @see {@link CursorColumns} */ public static nextIndentTabStop(visibleColumn: number, indentSize: number): number { diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index 30e2cdd1dd4..3d6668c6b60 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -21,10 +21,25 @@ export const editorSymbolHighlightBorder = registerColor('editor.symbolHighlight export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hcDark: Color.white, hcLight: '#0F4A85' }, nls.localize('caret', 'Color of the editor cursor.')); export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hcDark: '#e3e4e229', hcLight: '#CCCCCC' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); -export const editorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.')); -export const editorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.')); export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); +export const deprecatedEditorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'), false, nls.localize('deprecatedEditorIndentGuides', '\'editorIndentGuide.background\' is deprecated. Use \'editorIndentGuide.background1\' instead.')); +export const deprecatedEditorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'), false, nls.localize('deprecatedEditorActiveIndentGuide', '\'editorIndentGuide.activeBackground\' is deprecated. Use \'editorIndentGuide.activeBackground1\' instead.')); + +export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', { dark: deprecatedEditorIndentGuides, light: deprecatedEditorIndentGuides, hcDark: deprecatedEditorIndentGuides, hcLight: deprecatedEditorIndentGuides }, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); +export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); +export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); +export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); +export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); +export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); + +export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', { dark: deprecatedEditorActiveIndentGuides, light: deprecatedEditorActiveIndentGuides, hcDark: deprecatedEditorActiveIndentGuides, hcLight: deprecatedEditorActiveIndentGuides }, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); +export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); +export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); +export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); +export const editorActiveIndentGuide5 = registerColor('editorIndentGuide.activeBackground5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide5', 'Color of the active editor indentation guides (5).')); +export const editorActiveIndentGuide6 = registerColor('editorIndentGuide.activeBackground6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide6', 'Color of the active editor indentation guides (6).')); + const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', { dark: deprecatedEditorActiveLineNumber, light: deprecatedEditorActiveLineNumber, hcDark: deprecatedEditorActiveLineNumber, hcLight: deprecatedEditorActiveLineNumber }, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); export const editorDimmedLineNumber = registerColor('editorLineNumber.dimmedForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('editorDimmedLineNumber', 'Color of the final editor line when editor.renderFinalNewline is set to dimmed.')); diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index d39b5e1901e..c8779a2881a 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -219,6 +219,12 @@ export class LineRange { return result; } + public forEach(f: (lineNumber: number) => void): void { + for (let lineNumber = this.startLineNumber; lineNumber < this.endLineNumberExclusive; lineNumber++) { + f(lineNumber); + } + } + /** * @internal */ diff --git a/src/vs/editor/common/cursor/cursorMoveOperations.ts b/src/vs/editor/common/cursor/cursorMoveOperations.ts index a57cc00fad5..5699917a4f4 100644 --- a/src/vs/editor/common/cursor/cursorMoveOperations.ts +++ b/src/vs/editor/common/cursor/cursorMoveOperations.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CursorConfiguration, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from 'vs/editor/common/cursorCommon'; +import * as strings from 'vs/base/common/strings'; +import { Constants } from 'vs/base/common/uint'; import { CursorColumns } from 'vs/editor/common/core/cursorColumns'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as strings from 'vs/base/common/strings'; -import { Constants } from 'vs/base/common/uint'; import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/cursor/cursorAtomicMoveOperations'; +import { CursorConfiguration, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from 'vs/editor/common/cursorCommon'; import { PositionAffinity } from 'vs/editor/common/model'; export class CursorPosition { @@ -213,7 +213,15 @@ export class MoveOperations { column = cursor.position.column; } - const r = MoveOperations.down(config, model, lineNumber, column, cursor.leftoverVisibleColumns, linesCount, true); + let i = 0; + let r: CursorPosition; + do { + r = MoveOperations.down(config, model, lineNumber + i, column, cursor.leftoverVisibleColumns, linesCount, true); + const np = model.normalizePosition(new Position(r.lineNumber, r.column), PositionAffinity.None); + if (np.lineNumber > lineNumber) { + break; + } + } while (i++ < 10 && lineNumber + i < model.getLineCount()); return cursor.move(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns); } diff --git a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts index 78fc69c2044..61a05010ea2 100644 --- a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts +++ b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts @@ -5,6 +5,7 @@ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { ISequence, SequenceDiff } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/standardLinesDiffComputer'; export function optimizeSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { let result = sequenceDiffs; @@ -32,6 +33,77 @@ export function smoothenSequenceDiffs(sequence1: ISequence, sequence2: ISequence return result; } +export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { + let diffs = sequenceDiffs; + if (diffs.length === 0) { + return diffs; + } + + let counter = 0; + let shouldRepeat: boolean; + do { + shouldRepeat = false; + + const result: SequenceDiff[] = [ + diffs[0] + ]; + + for (let i = 1; i < diffs.length; i++) { + const cur = diffs[i]; + const lastResult = result[result.length - 1]; + + function shouldJoinDiffs(before: SequenceDiff, after: SequenceDiff): boolean { + const unchangedRange = new OffsetRange(lastResult.seq1Range.endExclusive, cur.seq1Range.start); + + const unchangedLineCount = sequence1.countLinesIn(unchangedRange); + if (unchangedLineCount > 5 || unchangedRange.length > 500) { + return false; + } + + const unchangedText = sequence1.getText(unchangedRange).trim(); + if (unchangedText.length > 20 || unchangedText.split(/\r\n|\r|\n/).length > 1) { + return false; + } + + const beforeLineCount1 = sequence1.countLinesIn(before.seq1Range); + const beforeSeq1Length = before.seq1Range.length; + const beforeLineCount2 = sequence2.countLinesIn(before.seq2Range); + const beforeSeq2Length = before.seq2Range.length; + + const afterLineCount1 = sequence1.countLinesIn(after.seq1Range); + const afterSeq1Length = after.seq1Range.length; + const afterLineCount2 = sequence2.countLinesIn(after.seq2Range); + const afterSeq2Length = after.seq2Range.length; + + // TODO: Maybe a neural net can be used to derive the result from these numbers + + const max = 2 * 40 + 50; + function cap(v: number): number { + return Math.min(v, max); + } + + if (Math.pow(Math.pow(cap(beforeLineCount1 * 40 + beforeSeq1Length), 1.5) + Math.pow(cap(beforeLineCount2 * 40 + beforeSeq2Length), 1.5), 1.5) + + Math.pow(Math.pow(cap(afterLineCount1 * 40 + afterSeq1Length), 1.5) + Math.pow(cap(afterLineCount2 * 40 + afterSeq2Length), 1.5), 1.5) > ((max ** 1.5) ** 1.5) * 1.3) { + return true; + } + return false; + } + + const shouldJoin = shouldJoinDiffs(lastResult, cur); + if (shouldJoin) { + shouldRepeat = true; + result[result.length - 1] = result[result.length - 1].join(cur); + } else { + result.push(cur); + } + } + + diffs = result; + } while (counter++ < 10 && shouldRepeat); + + return diffs; +} + /** * This function fixes issues like this: * ``` @@ -45,14 +117,16 @@ export function smoothenSequenceDiffs(sequence1: ISequence, sequence2: ISequence * Improved diff: [{Add ", Foo" after Bar}] */ export function joinSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { - const result: SequenceDiff[] = []; - if (sequenceDiffs.length > 0) { - result.push(sequenceDiffs[0]); + if (sequenceDiffs.length === 0) { + return sequenceDiffs; } + const result: SequenceDiff[] = []; + result.push(sequenceDiffs[0]); + // First move them all to the left as much as possible and join them if possible for (let i = 1; i < sequenceDiffs.length; i++) { - const prevResult = sequenceDiffs[i - 1]; + const prevResult = result[result.length - 1]; let cur = sequenceDiffs[i]; if (cur.seq1Range.isEmpty || cur.seq2Range.isEmpty) { diff --git a/src/vs/editor/common/diff/linesDiffComputer.ts b/src/vs/editor/common/diff/linesDiffComputer.ts index a096042419f..84505ab563a 100644 --- a/src/vs/editor/common/diff/linesDiffComputer.ts +++ b/src/vs/editor/common/diff/linesDiffComputer.ts @@ -140,19 +140,20 @@ export class RangeMapping { } } +// TODO@hediet: Make LineRangeMapping extend from this! export class SimpleLineRangeMapping { constructor( - public readonly originalRange: LineRange, - public readonly modifiedRange: LineRange, + public readonly original: LineRange, + public readonly modified: LineRange, ) { } public toString(): string { - return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`; + return `{${this.original.toString()}->${this.modified.toString()}}`; } public flip(): SimpleLineRangeMapping { - return new SimpleLineRangeMapping(this.modifiedRange, this.originalRange); + return new SimpleLineRangeMapping(this.modified, this.original); } } diff --git a/src/vs/editor/common/diff/standardLinesDiffComputer.ts b/src/vs/editor/common/diff/standardLinesDiffComputer.ts index 876506a60e2..d73dda91bc1 100644 --- a/src/vs/editor/common/diff/standardLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/standardLinesDiffComputer.ts @@ -11,7 +11,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { DateTimeout, ISequence, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/algorithms/dynamicProgrammingDiffing'; -import { optimizeSequenceDiffs, smoothenSequenceDiffs } from 'vs/editor/common/diff/algorithms/joinSequenceDiffs'; +import { optimizeSequenceDiffs, removeRandomMatches, smoothenSequenceDiffs } from 'vs/editor/common/diff/algorithms/joinSequenceDiffs'; import { MyersDiffAlgorithm } from 'vs/editor/common/diff/algorithms/myersDiffAlgorithm'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LineRangeMapping, LinesDiff, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; @@ -20,6 +20,25 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { private readonly myersDiffingAlgorithm = new MyersDiffAlgorithm(); computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff { + if (originalLines.length === 1 && originalLines[0].length === 0 || modifiedLines.length === 1 && modifiedLines[0].length === 0) { + return { + changes: [ + new LineRangeMapping( + new LineRange(1, originalLines.length + 1), + new LineRange(1, modifiedLines.length + 1), + [ + new RangeMapping( + new Range(1, 1, originalLines.length, originalLines[0].length + 1), + new Range(1, 1, modifiedLines.length, modifiedLines[0].length + 1) + ) + ] + ) + ], + hitTimeout: false, + moves: [], + }; + } + const timeout = options.maxComputationTimeMs === 0 ? InfiniteTimeout.instance : new DateTimeout(options.maxComputationTimeMs); const considerWhitespaceChanges = !options.ignoreTrimWhitespace; @@ -150,12 +169,41 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { } } + // Make sure all ranges are valid + assertFn(() => { + function validatePosition(pos: Position, lines: string[]): boolean { + if (pos.lineNumber < 1 || pos.lineNumber > lines.length) { return false; } + const line = lines[pos.lineNumber - 1]; + if (pos.column < 1 || pos.column > line.length + 1) { return false; } + return true; + } + + function validateRange(range: LineRange, lines: string[]): boolean { + if (range.startLineNumber < 1 || range.startLineNumber > lines.length + 1) { return false; } + if (range.endLineNumberExclusive < 1 || range.endLineNumberExclusive > lines.length + 1) { return false; } + return true; + } + + for (const c of changes) { + if (!c.innerChanges) { return false; } + for (const ic of c.innerChanges) { + const valid = validatePosition(ic.modifiedRange.getStartPosition(), modifiedLines) && validatePosition(ic.modifiedRange.getEndPosition(), modifiedLines) && + validatePosition(ic.originalRange.getStartPosition(), originalLines) && validatePosition(ic.originalRange.getEndPosition(), originalLines); + if (!valid) { return false; } + } + if (!validateRange(c.modifiedRange, modifiedLines) || !validateRange(c.originalRange, originalLines)) { + return false; + } + } + return true; + }); + return new LinesDiff(changes, moves, hitTimeout); } private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { - const slice1 = new Slice(originalLines, diff.seq1Range, considerWhitespaceChanges); - const slice2 = new Slice(modifiedLines, diff.seq2Range, considerWhitespaceChanges); + const slice1 = new LinesSliceCharSequence(originalLines, diff.seq1Range, considerWhitespaceChanges); + const slice2 = new LinesSliceCharSequence(modifiedLines, diff.seq2Range, considerWhitespaceChanges); const diffResult = slice1.length + slice2.length < 500 ? this.dynamicProgrammingDiffing.compute(slice1, slice2, timeout) @@ -165,6 +213,7 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { diffs = optimizeSequenceDiffs(slice1, slice2, diffs); diffs = coverFullWords(slice1, slice2, diffs); diffs = smoothenSequenceDiffs(slice1, slice2, diffs); + diffs = removeRandomMatches(slice1, slice2, diffs); const result = diffs.map( (d) => @@ -183,7 +232,7 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { } } -function coverFullWords(sequence1: Slice, sequence2: Slice, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +function coverFullWords(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { const additional: SequenceDiff[] = []; let lastModifiedWord: { added: number; deleted: number; count: number; s1Range: OffsetRange; s2Range: OffsetRange } | undefined = undefined; @@ -398,7 +447,7 @@ function getIndentation(str: string): number { return i; } -class Slice implements ISequence { +export class LinesSliceCharSequence implements ISequence { private readonly elements: number[] = []; private readonly firstCharOffsetByLineMinusOne: number[] = []; public readonly lineRange: OffsetRange; @@ -452,7 +501,11 @@ class Slice implements ISequence { } get text(): string { - return [...this.elements].map(e => String.fromCharCode(e)).join(''); + return this.getText(new OffsetRange(0, this.length)); + } + + getText(range: OffsetRange): string { + return this.elements.slice(range.start, range.endExclusive).map(e => String.fromCharCode(e)).join(''); } getElement(offset: number): number { @@ -540,6 +593,10 @@ class Slice implements ISequence { return new OffsetRange(start, end); } + + public countLinesIn(range: OffsetRange): number { + return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber; + } } function isWordChar(charCode: number): boolean { diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index 899ad35781f..c831d05fe21 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -27,6 +27,8 @@ export namespace EditorContextKeys { export const readOnly = new RawContextKey('editorReadonly', false, nls.localize('editorReadonly', "Whether the editor is read-only")); export const inDiffEditor = new RawContextKey('inDiffEditor', false, nls.localize('inDiffEditor', "Whether the context is a diff editor")); export const isEmbeddedDiffEditor = new RawContextKey('isEmbeddedDiffEditor', false, nls.localize('isEmbeddedDiffEditor', "Whether the context is an embedded diff editor")); + export const accessibleDiffViewerVisible = new RawContextKey('accessibleDiffViewerVisible', false, nls.localize('accessibleDiffViewerVisible', "Whether the accessible diff viewer is visible")); + export const diffEditorRenderSideBySideInlineBreakpointReached = new RawContextKey('diffEditorRenderSideBySideInlineBreakpointReached', false, nls.localize('diffEditorRenderSideBySideInlineBreakpointReached', "Whether the diff editor render side by side inline breakpoint is reached")); export const columnSelection = new RawContextKey('editorColumnSelection', false, nls.localize('editorColumnSelection', "Whether `editor.columnSelection` is enabled")); export const writable = readOnly.toNegated(); export const hasNonEmptySelection = new RawContextKey('editorHasSelection', false, nls.localize('editorHasSelection', "Whether the editor has text selected")); diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 60a04c23d0a..2e1b61be5d8 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -13,7 +13,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; +import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -21,6 +21,7 @@ import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import * as model from 'vs/editor/common/model'; import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry'; import { ContiguousMultilineTokens } from 'vs/editor/common/tokens/contiguousMultilineTokens'; +import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; @@ -79,22 +80,6 @@ export class EncodedTokenizationResult { } } -/** - * @internal - */ -export interface IBackgroundTokenizer extends IDisposable { - /** - * Instructs the background tokenizer to set the tokens for the given range again. - * - * This might be necessary if the renderer overwrote those tokens with heuristically computed ones for some viewport, - * when the change does not even propagate to that viewport. - */ - requestTokens(startLineNumber: number, endLineNumberExclusive: number): void; - - reportMismatchingTokens?(lineNumber: number): void; -} - - /** * @internal */ @@ -117,6 +102,21 @@ export interface ITokenizationSupport { createBackgroundTokenizer?(textModel: model.ITextModel, store: IBackgroundTokenizationStore): IBackgroundTokenizer | undefined; } +/** + * @internal + */ +export interface IBackgroundTokenizer extends IDisposable { + /** + * Instructs the background tokenizer to set the tokens for the given range again. + * + * This might be necessary if the renderer overwrote those tokens with heuristically computed ones for some viewport, + * when the change does not even propagate to that viewport. + */ + requestTokens(startLineNumber: number, endLineNumberExclusive: number): void; + + reportMismatchingTokens?(lineNumber: number): void; +} + /** * @internal */ @@ -703,6 +703,8 @@ export interface InlineCompletions { provideInlineCompletions(model: model.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; @@ -721,6 +723,20 @@ export interface InlineCompletionsProvider { - private readonly initialState = this.tokenizationSupport.getInitialState(); + private readonly initialState = this.tokenizationSupport.getInitialState() as TState; public readonly store: TrackingTokenizationStateStore; @@ -37,10 +37,11 @@ export class TokenizerWithStateStore { } public getStartState(lineNumber: number): TState | null { - if (lineNumber === 1) { - return this.initialState as TState; - } - return this.store.getEndState(lineNumber - 1); + return this.store.getStartState(lineNumber, this.initialState); + } + + public getFirstInvalidLine(): { lineNumber: number; startState: TState } | null { + return this.store.getFirstInvalidLine(this.initialState); } } @@ -58,17 +59,16 @@ export class TokenizerWithStateStoreAndTextModel const languageId = this._textModel.getLanguageId(); while (true) { - const nextLineNumber = this.store.getFirstInvalidEndStateLineNumber(); - if (!nextLineNumber || nextLineNumber > lineNumber) { + const lineToTokenize = this.getFirstInvalidLine(); + if (!lineToTokenize || lineToTokenize.lineNumber > lineNumber) { break; } - const text = this._textModel.getLineContent(nextLineNumber); - const lineStartState = this.getStartState(nextLineNumber); + const text = this._textModel.getLineContent(lineToTokenize.lineNumber); - const r = safeTokenize(this._languageIdCodec, languageId, this.tokenizationSupport, text, true, lineStartState!); - builder.add(nextLineNumber, r.tokens); - this!.store.setEndState(nextLineNumber, r.endState as TState); + const r = safeTokenize(this._languageIdCodec, languageId, this.tokenizationSupport, text, true, lineToTokenize.startState); + builder.add(lineToTokenize.lineNumber, r.tokens); + this.store.setEndState(lineToTokenize.lineNumber, r.endState as TState); } } @@ -204,8 +204,13 @@ export class TokenizerWithStateStoreAndTextModel } } +/** + * **Invariant:** + * If the text model is retokenized from line 1 to {@link getFirstInvalidEndStateLineNumber}() - 1, + * then the recomputed end state for line l will be equal to {@link getEndState}(l). + */ export class TrackingTokenizationStateStore { - private readonly tokenizationStateStore = new TokenizationStateStore(); + private readonly _tokenizationStateStore = new TokenizationStateStore(); private readonly _invalidEndStatesLineNumbers = new RangePriorityQueueImpl(); constructor(private lineCount: number) { @@ -213,20 +218,19 @@ export class TrackingTokenizationStateStore { } public getEndState(lineNumber: number): TState | null { - return this.tokenizationStateStore.getEndState(lineNumber); + return this._tokenizationStateStore.getEndState(lineNumber); } + /** + * @returns if the end state has changed. + */ public setEndState(lineNumber: number, state: TState): boolean { - while (true) { - const min = this._invalidEndStatesLineNumbers.min; - if (min !== null && min <= lineNumber) { - this._invalidEndStatesLineNumbers.removeMin(); - } else { - break; - } + if (!state) { + throw new BugIndicatingError('Cannot set null/undefined state'); } - const r = this.tokenizationStateStore.setEndState(lineNumber, state); + this._invalidEndStatesLineNumbers.delete(lineNumber); + const r = this._tokenizationStateStore.setEndState(lineNumber, state); if (r && lineNumber < this.lineCount) { // because the state changed, we cannot trust the next state anymore and have to invalidate it. this._invalidEndStatesLineNumbers.addRange(new OffsetRange(lineNumber + 1, lineNumber + 2)); @@ -237,7 +241,7 @@ export class TrackingTokenizationStateStore { public acceptChange(range: LineRange, newLineCount: number): void { this.lineCount += newLineCount - range.length; - this.tokenizationStateStore.acceptChange(range, newLineCount); + this._tokenizationStateStore.acceptChange(range, newLineCount); this._invalidEndStatesLineNumbers.addRangeAndResize(new OffsetRange(range.startLineNumber, range.endLineNumberExclusive), newLineCount); } @@ -252,16 +256,30 @@ export class TrackingTokenizationStateStore { this._invalidEndStatesLineNumbers.addRange(new OffsetRange(range.startLineNumber, range.endLineNumberExclusive)); } - public getFirstInvalidEndStateLineNumber(): number | null { - return this._invalidEndStatesLineNumbers.min; - } + public getFirstInvalidEndStateLineNumber(): number | null { return this._invalidEndStatesLineNumbers.min; } public getFirstInvalidEndStateLineNumberOrMax(): number { - return this._invalidEndStatesLineNumbers.min || Number.MAX_SAFE_INTEGER; + return this.getFirstInvalidEndStateLineNumber() || Number.MAX_SAFE_INTEGER; } - public isTokenizationComplete(): boolean { - return this._invalidEndStatesLineNumbers.min === null; + public allStatesValid(): boolean { return this._invalidEndStatesLineNumbers.min === null; } + + public getStartState(lineNumber: number, initialState: TState): TState | null { + if (lineNumber === 1) { return initialState; } + return this.getEndState(lineNumber - 1); + } + + public getFirstInvalidLine(initialState: TState): { lineNumber: number; startState: TState } | null { + const lineNumber = this.getFirstInvalidEndStateLineNumber(); + if (lineNumber === null) { + return null; + } + const startState = this.getStartState(lineNumber, initialState); + if (!startState) { + throw new BugIndicatingError('Start state must be defined'); + } + + return { lineNumber, startState }; } } @@ -338,6 +356,26 @@ export class RangePriorityQueueImpl implements RangePriorityQueue { return range.start; } + public delete(value: number): void { + const idx = this._ranges.findIndex(r => r.contains(value)); + if (idx !== -1) { + const range = this._ranges[idx]; + if (range.start === value) { + if (range.endExclusive === value + 1) { + this._ranges.splice(idx, 1); + } else { + this._ranges[idx] = new OffsetRange(value + 1, range.endExclusive); + } + } else { + if (range.endExclusive === value + 1) { + this._ranges[idx] = new OffsetRange(range.start, value); + } else { + this._ranges.splice(idx, 1, new OffsetRange(range.start, value), new OffsetRange(value + 1, range.endExclusive)); + } + } + } + } + public addRange(range: OffsetRange): void { OffsetRange.addRange(range, this._ranges); } @@ -490,23 +528,23 @@ export class DefaultBackgroundTokenizer implements IBackgroundTokenizer { if (!this._tokenizerWithStateStore) { return false; } - return !this._tokenizerWithStateStore.store.isTokenizationComplete(); + return !this._tokenizerWithStateStore.store.allStatesValid(); } private _tokenizeOneInvalidLine(builder: ContiguousMultilineTokensBuilder): number { - if (!this._tokenizerWithStateStore || !this._hasLinesToTokenize()) { + const firstInvalidLine = this._tokenizerWithStateStore?.getFirstInvalidLine(); + if (!firstInvalidLine) { return this._tokenizerWithStateStore._textModel.getLineCount() + 1; } - const lineNumber = this._tokenizerWithStateStore.store.getFirstInvalidEndStateLineNumber()!; - this._tokenizerWithStateStore.updateTokensUntilLine(builder, lineNumber); - return lineNumber; + this._tokenizerWithStateStore.updateTokensUntilLine(builder, firstInvalidLine.lineNumber); + return firstInvalidLine.lineNumber; } public checkFinished(): void { if (this._isDisposed) { return; } - if (this._tokenizerWithStateStore.store.isTokenizationComplete()) { + if (this._tokenizerWithStateStore.store.allStatesValid()) { this._backgroundTokenStore.backgroundTokenizationFinished(); } } diff --git a/src/vs/editor/common/model/tokenizationTextModelPart.ts b/src/vs/editor/common/model/tokenizationTextModelPart.ts index e077e75afe1..54476c26f20 100644 --- a/src/vs/editor/common/model/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/model/tokenizationTextModelPart.ts @@ -449,12 +449,10 @@ class GrammarTokens extends Disposable { this._onDidChangeBackgroundTokenizationState.fire(); }, setEndState: (lineNumber, state) => { - if (!state) { - throw new BugIndicatingError(); - } - const firstInvalidEndStateLineNumber = this._tokenizer?.store.getFirstInvalidEndStateLineNumber() ?? undefined; - if (firstInvalidEndStateLineNumber !== undefined && lineNumber >= firstInvalidEndStateLineNumber) { - // Don't accept states for definitely valid states + if (!this._tokenizer) { return; } + const firstInvalidEndStateLineNumber = this._tokenizer.store.getFirstInvalidEndStateLineNumber(); + // Don't accept states for definitely valid states, the renderer is ahead of the worker! + if (firstInvalidEndStateLineNumber !== null && lineNumber >= firstInvalidEndStateLineNumber) { this._tokenizer?.store.setEndState(lineNumber, state); } }, @@ -642,7 +640,7 @@ class AttachedViewHandler extends Disposable { } private update(): void { - if (equals(this._computedLineRanges, this._lineRanges)) { + if (equals(this._computedLineRanges, this._lineRanges, (a, b) => a.equals(b))) { return; } this._computedLineRanges = this._lineRanges; diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 2f51814946b..2c26721fd47 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -440,10 +440,10 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { quitEarly: result.hitTimeout, changes: getLineChanges(result.changes), moves: result.moves.map(m => ([ - m.lineRangeMapping.originalRange.startLineNumber, - m.lineRangeMapping.originalRange.endLineNumberExclusive, - m.lineRangeMapping.modifiedRange.startLineNumber, - m.lineRangeMapping.modifiedRange.endLineNumberExclusive, + m.lineRangeMapping.original.startLineNumber, + m.lineRangeMapping.original.endLineNumberExclusive, + m.lineRangeMapping.modified.startLineNumber, + m.lineRangeMapping.modified.endLineNumberExclusive, getLineChanges(m.changes) ])), }; diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index d48dc8f7aa1..d92f0c1272e 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -178,146 +178,147 @@ export enum EditorOption { accessibilitySupport = 2, accessibilityPageSize = 3, ariaLabel = 4, - autoClosingBrackets = 5, - screenReaderAnnounceInlineSuggestion = 6, - autoClosingDelete = 7, - autoClosingOvertype = 8, - autoClosingQuotes = 9, - autoIndent = 10, - automaticLayout = 11, - autoSurround = 12, - bracketPairColorization = 13, - guides = 14, - codeLens = 15, - codeLensFontFamily = 16, - codeLensFontSize = 17, - colorDecorators = 18, - colorDecoratorsLimit = 19, - columnSelection = 20, - comments = 21, - contextmenu = 22, - copyWithSyntaxHighlighting = 23, - cursorBlinking = 24, - cursorSmoothCaretAnimation = 25, - cursorStyle = 26, - cursorSurroundingLines = 27, - cursorSurroundingLinesStyle = 28, - cursorWidth = 29, - disableLayerHinting = 30, - disableMonospaceOptimizations = 31, - domReadOnly = 32, - dragAndDrop = 33, - dropIntoEditor = 34, - emptySelectionClipboard = 35, - experimentalWhitespaceRendering = 36, - extraEditorClassName = 37, - fastScrollSensitivity = 38, - find = 39, - fixedOverflowWidgets = 40, - folding = 41, - foldingStrategy = 42, - foldingHighlight = 43, - foldingImportsByDefault = 44, - foldingMaximumRegions = 45, - unfoldOnClickAfterEndOfLine = 46, - fontFamily = 47, - fontInfo = 48, - fontLigatures = 49, - fontSize = 50, - fontWeight = 51, - fontVariations = 52, - formatOnPaste = 53, - formatOnType = 54, - glyphMargin = 55, - gotoLocation = 56, - hideCursorInOverviewRuler = 57, - hover = 58, - inDiffEditor = 59, - inlineSuggest = 60, - letterSpacing = 61, - lightbulb = 62, - lineDecorationsWidth = 63, - lineHeight = 64, - lineNumbers = 65, - lineNumbersMinChars = 66, - linkedEditing = 67, - links = 68, - matchBrackets = 69, - minimap = 70, - mouseStyle = 71, - mouseWheelScrollSensitivity = 72, - mouseWheelZoom = 73, - multiCursorMergeOverlapping = 74, - multiCursorModifier = 75, - multiCursorPaste = 76, - multiCursorLimit = 77, - occurrencesHighlight = 78, - overviewRulerBorder = 79, - overviewRulerLanes = 80, - padding = 81, - pasteAs = 82, - parameterHints = 83, - peekWidgetDefaultFocus = 84, - definitionLinkOpensInPeek = 85, - quickSuggestions = 86, - quickSuggestionsDelay = 87, - readOnly = 88, - readOnlyMessage = 89, - renameOnType = 90, - renderControlCharacters = 91, - renderFinalNewline = 92, - renderLineHighlight = 93, - renderLineHighlightOnlyWhenFocus = 94, - renderValidationDecorations = 95, - renderWhitespace = 96, - revealHorizontalRightPadding = 97, - roundedSelection = 98, - rulers = 99, - scrollbar = 100, - scrollBeyondLastColumn = 101, - scrollBeyondLastLine = 102, - scrollPredominantAxis = 103, - selectionClipboard = 104, - selectionHighlight = 105, - selectOnLineNumbers = 106, - showFoldingControls = 107, - showUnused = 108, - snippetSuggestions = 109, - smartSelect = 110, - smoothScrolling = 111, - stickyScroll = 112, - stickyTabStops = 113, - stopRenderingLineAfter = 114, - suggest = 115, - suggestFontSize = 116, - suggestLineHeight = 117, - suggestOnTriggerCharacters = 118, - suggestSelection = 119, - tabCompletion = 120, - tabIndex = 121, - unicodeHighlighting = 122, - unusualLineTerminators = 123, - useShadowDOM = 124, - useTabStops = 125, - wordBreak = 126, - wordSeparators = 127, - wordWrap = 128, - wordWrapBreakAfterCharacters = 129, - wordWrapBreakBeforeCharacters = 130, - wordWrapColumn = 131, - wordWrapOverride1 = 132, - wordWrapOverride2 = 133, - wrappingIndent = 134, - wrappingStrategy = 135, - showDeprecated = 136, - inlayHints = 137, - editorClassName = 138, - pixelRatio = 139, - tabFocusMode = 140, - layoutInfo = 141, - wrappingInfo = 142, - defaultColorDecorators = 143, - colorDecoratorsActivatedOn = 144 + ariaRequired = 5, + autoClosingBrackets = 6, + screenReaderAnnounceInlineSuggestion = 7, + autoClosingDelete = 8, + autoClosingOvertype = 9, + autoClosingQuotes = 10, + autoIndent = 11, + automaticLayout = 12, + autoSurround = 13, + bracketPairColorization = 14, + guides = 15, + codeLens = 16, + codeLensFontFamily = 17, + codeLensFontSize = 18, + colorDecorators = 19, + colorDecoratorsLimit = 20, + columnSelection = 21, + comments = 22, + contextmenu = 23, + copyWithSyntaxHighlighting = 24, + cursorBlinking = 25, + cursorSmoothCaretAnimation = 26, + cursorStyle = 27, + cursorSurroundingLines = 28, + cursorSurroundingLinesStyle = 29, + cursorWidth = 30, + disableLayerHinting = 31, + disableMonospaceOptimizations = 32, + domReadOnly = 33, + dragAndDrop = 34, + dropIntoEditor = 35, + emptySelectionClipboard = 36, + experimentalWhitespaceRendering = 37, + extraEditorClassName = 38, + fastScrollSensitivity = 39, + find = 40, + fixedOverflowWidgets = 41, + folding = 42, + foldingStrategy = 43, + foldingHighlight = 44, + foldingImportsByDefault = 45, + foldingMaximumRegions = 46, + unfoldOnClickAfterEndOfLine = 47, + fontFamily = 48, + fontInfo = 49, + fontLigatures = 50, + fontSize = 51, + fontWeight = 52, + fontVariations = 53, + formatOnPaste = 54, + formatOnType = 55, + glyphMargin = 56, + gotoLocation = 57, + hideCursorInOverviewRuler = 58, + hover = 59, + inDiffEditor = 60, + inlineSuggest = 61, + letterSpacing = 62, + lightbulb = 63, + lineDecorationsWidth = 64, + lineHeight = 65, + lineNumbers = 66, + lineNumbersMinChars = 67, + linkedEditing = 68, + links = 69, + matchBrackets = 70, + minimap = 71, + mouseStyle = 72, + mouseWheelScrollSensitivity = 73, + mouseWheelZoom = 74, + multiCursorMergeOverlapping = 75, + multiCursorModifier = 76, + multiCursorPaste = 77, + multiCursorLimit = 78, + occurrencesHighlight = 79, + overviewRulerBorder = 80, + overviewRulerLanes = 81, + padding = 82, + pasteAs = 83, + parameterHints = 84, + peekWidgetDefaultFocus = 85, + definitionLinkOpensInPeek = 86, + quickSuggestions = 87, + quickSuggestionsDelay = 88, + readOnly = 89, + readOnlyMessage = 90, + renameOnType = 91, + renderControlCharacters = 92, + renderFinalNewline = 93, + renderLineHighlight = 94, + renderLineHighlightOnlyWhenFocus = 95, + renderValidationDecorations = 96, + renderWhitespace = 97, + revealHorizontalRightPadding = 98, + roundedSelection = 99, + rulers = 100, + scrollbar = 101, + scrollBeyondLastColumn = 102, + scrollBeyondLastLine = 103, + scrollPredominantAxis = 104, + selectionClipboard = 105, + selectionHighlight = 106, + selectOnLineNumbers = 107, + showFoldingControls = 108, + showUnused = 109, + snippetSuggestions = 110, + smartSelect = 111, + smoothScrolling = 112, + stickyScroll = 113, + stickyTabStops = 114, + stopRenderingLineAfter = 115, + suggest = 116, + suggestFontSize = 117, + suggestLineHeight = 118, + suggestOnTriggerCharacters = 119, + suggestSelection = 120, + tabCompletion = 121, + tabIndex = 122, + unicodeHighlighting = 123, + unusualLineTerminators = 124, + useShadowDOM = 125, + useTabStops = 126, + wordBreak = 127, + wordSeparators = 128, + wordWrap = 129, + wordWrapBreakAfterCharacters = 130, + wordWrapBreakBeforeCharacters = 131, + wordWrapColumn = 132, + wordWrapOverride1 = 133, + wordWrapOverride2 = 134, + wrappingIndent = 135, + wrappingStrategy = 136, + showDeprecated = 137, + inlayHints = 138, + editorClassName = 139, + pixelRatio = 140, + tabFocusMode = 141, + layoutInfo = 142, + wrappingInfo = 143, + defaultColorDecorators = 144, + colorDecoratorsActivatedOn = 145 } /** diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index 5bc2ddd91de..f2251207711 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -12,10 +12,12 @@ export namespace AccessibilityHelpNLS { export const editableDiffEditor = nls.localize("editableDiffEditor", "You are in a pane of a diff editor."); export const readonlyEditor = nls.localize("readonlyEditor", "You are in a read-only code editor"); export const editableEditor = nls.localize("editableEditor", "You are in a code editor"); - export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "To configure the editor to be optimized for usage with a Screen Reader press Command+E now."); - export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "To configure the editor to be optimized for usage with a Screen Reader press Control+E now."); - export const auto_on = nls.localize("auto_on", "The editor is configured to be optimized for usage with a Screen Reader."); - export const auto_off = nls.localize("auto_off", "The editor is configured to never be optimized for usage with a Screen Reader"); + export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "To configure the application to be optimized for usage with a Screen Reader press Command+E now."); + export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "To configure the application to be optimized for usage with a Screen Reader press Control+E now."); + export const auto_on = nls.localize("auto_on", "The application is configured to be optimized for usage with a Screen Reader."); + export const auto_off = nls.localize("auto_off", "The application is configured to never be optimized for usage with a Screen Reader"); + export const screenReaderModeEnabled = nls.localize("screenReaderModeEnabled", "Screen Reader Optimized Mode enabled."); + export const screenReaderModeDisabled = nls.localize("screenReaderModeDisabled", "Screen Reader Optimized Mode disabled."); export const tabFocusModeOnMsg = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); export const tabFocusModeOnMsgNoKb = nls.localize("tabFocusModeOnMsgNoKb", "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); diff --git a/src/vs/editor/common/tokenizationRegistry.ts b/src/vs/editor/common/tokenizationRegistry.ts index 2d5ab7781a5..d9fb1bba82f 100644 --- a/src/vs/editor/common/tokenizationRegistry.ts +++ b/src/vs/editor/common/tokenizationRegistry.ts @@ -78,8 +78,6 @@ export class TokenizationRegistry implements ITokenizationRegistry { return this.get(languageId); } - - public isResolved(languageId: string): boolean { const tokenizationSupport = this.get(languageId); if (tokenizationSupport) { diff --git a/src/vs/editor/common/viewModel/modelLineProjection.ts b/src/vs/editor/common/viewModel/modelLineProjection.ts index 81be5db0a10..e66499cd98e 100644 --- a/src/vs/editor/common/viewModel/modelLineProjection.ts +++ b/src/vs/editor/common/viewModel/modelLineProjection.ts @@ -193,7 +193,7 @@ class ModelLineProjection implements IModelLineProjection { if (options.inlineClassName) { const offset = (outputLineIndex > 0 ? lineBreakData.wrappedTextIndentLength : 0); const start = offset + Math.max(injectedTextStartOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, 0); - const end = offset + Math.min(injectedTextEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections); + const end = offset + Math.min(injectedTextEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections); if (start !== end) { inlineDecorations.push(new SingleLineInlineDecoration(start, end, options.inlineClassName, options.inlineClassNameAffectsLetterSpacing!)); } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index d441eef1d2e..14307f031e5 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -54,7 +54,7 @@ export class CodeActionController extends Disposable implements IEditorContribut private readonly _resolver: CodeActionKeybindingResolver; - #disposed = false; + private _disposed = false; constructor( editor: ICodeEditor, @@ -89,7 +89,7 @@ export class CodeActionController extends Disposable implements IEditorContribut } override dispose() { - this.#disposed = true; + this._disposed = true; super.dispose(); } @@ -148,7 +148,7 @@ export class CodeActionController extends Disposable implements IEditorContribut return; } - if (this.#disposed) { + if (this._disposed) { return; } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index 3979c4e4538..b21d5f2dc5d 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -145,7 +145,7 @@ export class CodeActionModel extends Disposable { private readonly _onDidChangeState = this._register(new Emitter()); public readonly onDidChangeState = this._onDidChangeState.event; - #isDisposed = false; + private _disposed = false; constructor( private readonly _editor: ICodeEditor, @@ -165,17 +165,17 @@ export class CodeActionModel extends Disposable { } override dispose(): void { - if (this.#isDisposed) { + if (this._disposed) { return; } - this.#isDisposed = true; + this._disposed = true; super.dispose(); this.setState(CodeActionsState.Empty, true); } private _update(): void { - if (this.#isDisposed) { + if (this._disposed) { return; } @@ -226,7 +226,7 @@ export class CodeActionModel extends Disposable { this._state = newState; - if (!skipNotify && !this.#isDisposed) { + if (!skipNotify && !this._disposed) { this._onDidChangeState.fire(newState); } } diff --git a/src/vs/editor/contrib/colorPicker/browser/colorContributions.ts b/src/vs/editor/contrib/colorPicker/browser/colorContributions.ts index 7ff5f54f248..2c041f1c749 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorContributions.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorContributions.ts @@ -60,7 +60,7 @@ export class ColorContribution extends Disposable implements IEditorContribution if (!hoverController) { return; } - if (!hoverController.isColorPickerVisible()) { + if (!hoverController.isColorPickerVisible) { const range = new Range(target.range.startLineNumber, target.range.startColumn + 1, target.range.endLineNumber, target.range.endColumn + 1); hoverController.showContentHover(range, HoverStartMode.Immediate, HoverStartSource.Mouse, false, true); } diff --git a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts index e321fae6dfd..982ecbf5053 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts @@ -20,6 +20,7 @@ import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IEditorHoverRend import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; +import { Dimension } from 'vs/base/browser/dom'; export class ColorHover implements IHoverPart { @@ -181,6 +182,10 @@ function renderHoverParts(participant: ColorHoverParticipant | StandaloneColorPi if (hoverParts.length === 0 || !editor.hasModel()) { return Disposable.None; } + if (context.setMinimumDimensions) { + const minimumHeight = editor.getOption(EditorOption.lineHeight) + 8; + context.setMinimumDimensions(new Dimension(302, minimumHeight)); + } const disposables = new DisposableStore(); const colorHover = hoverParts[0]; diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css index 62f891080fe..f484517caba 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css +++ b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css @@ -48,13 +48,19 @@ cursor: pointer; color: white; flex: 1; + white-space: nowrap; + overflow: hidden; +} + +.colorpicker-header .picked-color .picked-color-presentation { + white-space: nowrap; + margin-left: 5px; + margin-right: 5px; } .colorpicker-header .picked-color .codicon { color: inherit; font-size: 14px; - position: absolute; - left: 8px; } .colorpicker-header .picked-color.light { diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index 24b6e6408b3..2f25409976f 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -26,6 +26,7 @@ export class ColorPickerHeader extends Disposable { private readonly _domNode: HTMLElement; private readonly _pickedColorNode: HTMLElement; + private readonly _pickedColorPresentation: HTMLElement; private readonly _originalColorNode: HTMLElement; private readonly _closeButton: CloseButton | null = null; private backgroundColor: Color; @@ -37,6 +38,9 @@ export class ColorPickerHeader extends Disposable { dom.append(container, this._domNode); this._pickedColorNode = dom.append(this._domNode, $('.picked-color')); + dom.append(this._pickedColorNode, $('span.codicon.codicon-color-mode')); + this._pickedColorPresentation = dom.append(this._pickedColorNode, document.createElement('span')); + this._pickedColorPresentation.classList.add('picked-color-presentation'); const tooltip = localize('clickToToggleColorOptions', "Click to toggle color options (rgb/hsl/hex)"); this._pickedColorNode.setAttribute('title', tooltip); @@ -91,8 +95,7 @@ export class ColorPickerHeader extends Disposable { } private onDidChangePresentation(): void { - this._pickedColorNode.textContent = this.model.presentation ? this.model.presentation.label : ''; - this._pickedColorNode.prepend($('.codicon.codicon-color-mode')); + this._pickedColorPresentation.textContent = this.model.presentation ? this.model.presentation.label : ''; } } @@ -318,11 +321,13 @@ class SaturationBox extends Disposable { this.selection.style.top = `${this.height - v * this.height}px`; } - private onDidChangeColor(): void { + private onDidChangeColor(color: Color): void { if (this.monitor && this.monitor.isMonitoring()) { return; } this.paint(); + const hsva = color.hsva; + this.paintSelection(hsva.s, hsva.v); } } @@ -352,6 +357,7 @@ abstract class Strip extends Disposable { this.slider.style.top = `0px`; this._register(dom.addDisposableListener(this.domNode, dom.EventType.POINTER_DOWN, e => this.onPointerDown(e))); + this._register(model.onDidChangeColor(this.onDidChangeColor, this)); this.layout(); } @@ -362,6 +368,11 @@ abstract class Strip extends Disposable { this.updateSliderPosition(value); } + protected onDidChangeColor(color: Color) { + const value = this.getValue(color); + this.updateSliderPosition(value); + } + private onPointerDown(e: PointerEvent): void { if (!e.target || !(e.target instanceof Element)) { return; @@ -404,11 +415,11 @@ class OpacityStrip extends Strip { super(container, model, showingStandaloneColorPicker); this.domNode.classList.add('opacity-strip'); - this._register(model.onDidChangeColor(this.onDidChangeColor, this)); this.onDidChangeColor(this.model.color); } - private onDidChangeColor(color: Color): void { + protected override onDidChangeColor(color: Color): void { + super.onDidChangeColor(color); const { r, g, b } = color.rgba; const opaque = new Color(new RGBA(r, g, b, 1)); const transparent = new Color(new RGBA(r, g, b, 0)); diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index 4eef122381b..e6f6c2b49e4 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAction, Separator, SubmenuAction } from 'vs/base/common/actions'; @@ -103,7 +103,7 @@ export class ContextMenuController implements IEditorContribution { e.event.stopPropagation(); if (e.target.type === MouseTargetType.SCROLLBAR) { - return this._showScrollbarContextMenu({ x: e.event.posx - 1, width: 2, y: e.event.posy - 1, height: 2 }); + return this._showScrollbarContextMenu(e.event); } if (e.target.type !== MouseTargetType.CONTENT_TEXT && e.target.type !== MouseTargetType.CONTENT_EMPTY && e.target.type !== MouseTargetType.TEXTAREA) { @@ -129,16 +129,16 @@ export class ContextMenuController implements IEditorContribution { } // Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position - let anchor: IAnchor | null = null; + let anchor: IMouseEvent | null = null; if (e.target.type !== MouseTargetType.TEXTAREA) { - anchor = { x: e.event.posx - 1, width: 2, y: e.event.posy - 1, height: 2 }; + anchor = e.event; } // Show the context menu this.showContextMenu(anchor); } - public showContextMenu(anchor?: IAnchor | null): void { + public showContextMenu(anchor?: IMouseEvent | null): void { if (!this._editor.getOption(EditorOption.contextmenu)) { return; // Context menu is turned off through configuration } @@ -193,7 +193,7 @@ export class ContextMenuController implements IEditorContribution { return result; } - private _doShowContextMenu(actions: IAction[], anchor: IAnchor | null = null): void { + private _doShowContextMenu(actions: IAction[], event: IMouseEvent | null = null): void { if (!this._editor.hasModel()) { return; } @@ -206,6 +206,7 @@ export class ContextMenuController implements IEditorContribution { } }); + let anchor: IMouseEvent | IAnchor | null = event; if (!anchor) { // Ensure selection is visible this._editor.revealPosition(this._editor.getPosition(), ScrollType.Immediate); @@ -259,7 +260,7 @@ export class ContextMenuController implements IEditorContribution { }); } - private _showScrollbarContextMenu(anchor: IAnchor): void { + private _showScrollbarContextMenu(anchor: IMouseEvent): void { if (!this._editor.hasModel()) { return; } diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index 24bffebd3ca..3ff93ce28e1 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -11,7 +11,9 @@ import { UriList, VSDataTransfer, createStringDataTransferItem, matchesMimeType import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import * as platform from 'vs/base/common/platform'; +import { withUndefinedAsNull } from 'vs/base/common/types'; import { generateUuid } from 'vs/base/common/uuid'; +import { ClipboardEventUtils } from 'vs/editor/browser/controller/textAreaInput'; import { toExternalVSDataTransfer, toVSDataTransfer } from 'vs/editor/browser/dnd'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; @@ -220,7 +222,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi return; } - const metadata = this.fetchCopyMetadata(e.clipboardData); + const metadata = this.fetchCopyMetadata(e); const dataTransfer = toExternalVSDataTransfer(e.clipboardData); dataTransfer.delete(vscodeClipboardMime); @@ -282,6 +284,12 @@ export class CopyPasteController extends Disposable implements IEditorContributi return; } + // If the only edit returned is a text edit, use the default paste handler + if (providerEdits.length === 1 && providerEdits[0].id === 'text') { + await this.applyDefaultPasteHandler(dataTransfer, metadata, tokenSource.token); + return; + } + if (providerEdits.length) { const canShowWidget = editor.getOption(EditorOption.pasteAs).showPasteSelector === 'afterPaste'; return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections, { activeEditIndex: 0, allEdits: providerEdits }, canShowWidget, tokenSource.token); @@ -369,8 +377,13 @@ export class CopyPasteController extends Disposable implements IEditorContributi dataTransfer.setData(vscodeClipboardMime, JSON.stringify(metadata)); } - private fetchCopyMetadata(dataTransfer: DataTransfer): CopyMetadata | undefined { - const rawMetadata = dataTransfer.getData(vscodeClipboardMime); + private fetchCopyMetadata(e: ClipboardEvent): CopyMetadata | undefined { + if (!e.clipboardData) { + return; + } + + // Prefer using the clipboard data we saved off + const rawMetadata = e.clipboardData.getData(vscodeClipboardMime); if (rawMetadata) { try { return JSON.parse(rawMetadata); @@ -378,6 +391,19 @@ export class CopyPasteController extends Disposable implements IEditorContributi return undefined; } } + + // Otherwise try to extract the generic text editor metadata + const [_, metadata] = ClipboardEventUtils.getTextData(e.clipboardData); + if (metadata) { + return { + defaultPastePayload: { + mode: metadata.mode, + multicursorText: withUndefinedAsNull(metadata.multicursorText), + pasteOnNewLine: !!metadata.isFromEmptySelection, + }, + }; + } + return undefined; } diff --git a/src/vs/editor/contrib/find/test/browser/findController.test.ts b/src/vs/editor/contrib/find/test/browser/findController.test.ts index 2cfca11f547..86f44277667 100644 --- a/src/vs/editor/contrib/find/test/browser/findController.test.ts +++ b/src/vs/editor/contrib/find/test/browser/findController.test.ts @@ -63,14 +63,14 @@ function executeAction(instantiationService: IInstantiationService, editor: ICod }); } -suite('FindController', async () => { +suite('FindController', () => { const queryState: { [key: string]: any } = {}; let clipboardState = ''; const serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, onDidChangeTarget: Event.None, - onDidChangeValue: Event.None, + onDidChangeValue: () => Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], @@ -495,7 +495,7 @@ suite('FindController', async () => { }); }); -suite('FindController query options persistence', async () => { +suite('FindController query options persistence', () => { let queryState: { [key: string]: any } = {}; queryState['editor.isRegex'] = false; queryState['editor.matchCase'] = false; @@ -504,7 +504,7 @@ suite('FindController query options persistence', async () => { serviceCollection.set(IStorageService, { _serviceBrand: undefined, onDidChangeTarget: Event.None, - onDidChangeValue: Event.None, + onDidChangeValue: () => Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 825e60a7fd7..d852003494c 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -30,7 +30,7 @@ export class ContentHoverController extends Disposable { private readonly _participants: IEditorHoverParticipant[]; - private readonly _widget = this._register(this._instantiationService.createInstance(ContentHoverWidget, this._editor)); + private readonly _widget: ContentHoverWidget; getWidgetContent(): string | undefined { const node = this._widget.getDomNode(); @@ -52,6 +52,11 @@ export class ContentHoverController extends Disposable { ) { super(); + const minimumHeight = this._editor.getOption(EditorOption.lineHeight) + 8; + const minimumWidth = 4 / 3 * minimumHeight; + const minimumSize = new dom.Dimension(minimumWidth, minimumHeight); + this._widget = this._register(this._instantiationService.createInstance(ContentHoverWidget, this._editor, minimumSize)); + // Instantiate participants and sort them by `hoverOrdinal` which is relevant for rendering order. this._participants = []; for (const participant of HoverParticipantRegistry.getAll()) { @@ -215,18 +220,26 @@ export class ContentHoverController extends Disposable { this._setCurrentResult(null); } - public isColorPickerVisible(): boolean { + public get isColorPickerVisible(): boolean { return this._widget.isColorPickerVisible; } - public isVisibleFromKeyboard(): boolean { + public get isVisibleFromKeyboard(): boolean { return this._widget.isVisibleFromKeyboard; } - public isVisible(): boolean { + public get isVisible(): boolean { return this._widget.isVisible; } + public get isFocused(): boolean { + return this._widget.isFocused; + } + + public get isResizing(): boolean { + return this._widget.isResizing; + } + public containsNode(node: Node | null | undefined): boolean { return (node ? this._widget.getDomNode().contains(node) : false); } @@ -276,6 +289,7 @@ export class ContentHoverController extends Disposable { statusBar, setColorPicker: (widget) => colorPicker = widget, onContentsChanged: () => this._widget.onContentsChanged(), + setMinimumDimensions: (dimensions: dom.Dimension) => this._widget.setMinimumDimensions(dimensions), hide: () => this.hide() }; @@ -395,10 +409,6 @@ export class ContentHoverController extends Disposable { public goToBottom(): void { this._widget.goToBottom(); } - - public escape(): void { - this._widget.escape(); - } } class HoverResult { @@ -459,6 +469,7 @@ const CONTAINER_HEIGHT_PADDING = 6; export class ContentHoverWidget extends ResizableContentWidget { public static ID = 'editor.contrib.resizableContentHoverWidget'; + private static _lastDimensions: dom.Dimension = new dom.Dimension(0, 0); private _visibleData: ContentHoverVisibleData | undefined; private _positionPreference: ContentWidgetPositionPreference | undefined; @@ -479,11 +490,16 @@ export class ContentHoverWidget extends ResizableContentWidget { return this._hoverVisibleKey.get() ?? false; } + public get isFocused(): boolean { + return this._hoverFocusedKey.get() ?? false; + } + constructor( editor: ICodeEditor, + minimumSize: dom.Dimension, @IContextKeyService contextKeyService: IContextKeyService ) { - super(editor); + super(editor, minimumSize); this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(contextKeyService); this._hoverFocusedKey = EditorContextKeys.hoverFocused.bindTo(contextKeyService); @@ -541,12 +557,18 @@ export class ContentHoverWidget extends ResizableContentWidget { this._layoutContentWidget(); } - private _setContentsDomNodeMaxDimensions(width: number | string, height: number | string): void { + private static _applyMaxDimensions(container: HTMLElement, width: number | string, height: number | string) { const transformedWidth = typeof width === 'number' ? `${width}px` : width; const transformedHeight = typeof height === 'number' ? `${height}px` : height; - const contentsDomNode = this._hover.contentsDomNode; - contentsDomNode.style.maxWidth = transformedWidth; - contentsDomNode.style.maxHeight = transformedHeight; + container.style.maxWidth = transformedWidth; + container.style.maxHeight = transformedHeight; + } + + private _setHoverWidgetMaxDimensions(width: number | string, height: number | string): void { + ContentHoverWidget._applyMaxDimensions(this._hover.contentsDomNode, width, height); + ContentHoverWidget._applyMaxDimensions(this._hover.containerDomNode, width, height); + this._hover.containerDomNode.style.setProperty('--hover-maxWidth', typeof width === 'number' ? `${width}px` : width); + this._layoutContentWidget(); } private _hasHorizontalScrollbar(): boolean { @@ -564,7 +586,7 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _setAdjustedHoverWidgetDimensions(size: dom.Dimension): void { - this._setContentsDomNodeMaxDimensions('none', 'none'); + this._setHoverWidgetMaxDimensions('none', 'none'); const width = size.width; const height = size.height; this._setHoverWidgetDimensions(width, height); @@ -579,9 +601,11 @@ export class ContentHoverWidget extends ResizableContentWidget { const maxRenderingWidth = this._findMaximumRenderingWidth() ?? Infinity; const maxRenderingHeight = this._findMaximumRenderingHeight() ?? Infinity; this._resizableNode.maxSize = new dom.Dimension(maxRenderingWidth, maxRenderingHeight); + this._setHoverWidgetMaxDimensions(maxRenderingWidth, maxRenderingHeight); } protected override _resize(size: dom.Dimension): void { + ContentHoverWidget._lastDimensions = new dom.Dimension(size.width, size.height); this._setAdjustedHoverWidgetDimensions(size); this._resizableNode.layout(size.height, size.width); this._setResizableNodeMaxDimensions(); @@ -618,10 +642,9 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!this._editor || !this._editor.hasModel()) { return; } - const editorBox = dom.getDomNodePagePosition(this._editor.getDomNode()); - const glyphMarginWidth = this._editor.getLayoutInfo().glyphMarginWidth; - const leftOfContainer = this._hover.containerDomNode.offsetLeft; - return editorBox.width + editorBox.left - leftOfContainer - glyphMarginWidth; + const bodyBoxWidth = dom.getClientArea(document.body).width; + const horizontalPadding = 14; + return bodyBoxWidth - horizontalPadding; } public isMouseGettingCloser(posx: number, posy: number): boolean { @@ -655,12 +678,11 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _layout(): void { - const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); const { fontSize, lineHeight } = this._editor.getOption(EditorOption.fontInfo); const contentsDomNode = this._hover.contentsDomNode; contentsDomNode.style.fontSize = `${fontSize}px`; contentsDomNode.style.lineHeight = `${lineHeight / fontSize}`; - this._setContentsDomNodeMaxDimensions(Math.max(this._editor.getLayoutInfo().width * 0.66, 500), height); + this._updateMaxDimensions(); } private _updateFont(): void { @@ -680,17 +702,17 @@ export class ContentHoverWidget extends ResizableContentWidget { this._hover.onContentsChanged(); } - private _updateContentsDomNodeMaxDimensions() { - const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500); - const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); - this._setContentsDomNodeMaxDimensions(width, height); + private _updateMaxDimensions() { + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastDimensions.height); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastDimensions.width); + this._setHoverWidgetMaxDimensions(width, height); } private _render(node: DocumentFragment, hoverData: ContentHoverVisibleData) { this._setHoverData(hoverData); this._updateFont(); this._updateContent(node); - this._updateContentsDomNodeMaxDimensions(); + this._updateMaxDimensions(); this.onContentsChanged(); // Simply force a synchronous render on the editor // such that the widget does not really render with left = '0px' @@ -731,7 +753,7 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!this._visibleData) { return; } - const stoleFocus = this._visibleData.stoleFocus; + const stoleFocus = this._visibleData.stoleFocus || this._hoverFocusedKey.get(); this._setHoverData(undefined); this._resizableNode.maxSize = new dom.Dimension(Infinity, Infinity); this._resizableNode.clearSashHoverState(); @@ -757,6 +779,10 @@ export class ContentHoverWidget extends ResizableContentWidget { this._setContentsDomNodeDimensions(dom.getTotalWidth(contentsDomNode), Math.min(maxRenderingHeight, height - SCROLLBAR_WIDTH)); } + public setMinimumDimensions(dimensions: dom.Dimension): void { + this._resizableNode.minSize = dimensions; + } + public onContentsChanged(): void { this._removeConstraintsRenderNormally(); const containerDomNode = this._hover.containerDomNode; @@ -775,6 +801,10 @@ export class ContentHoverWidget extends ResizableContentWidget { this._adjustContentsBottomPadding(); this._adjustHoverHeightForScrollbar(height); } + if (this._visibleData?.showAtPosition) { + const widgetHeight = dom.getTotalHeight(this._hover.containerDomNode); + this._positionPreference = this._findPositionPreference(widgetHeight, this._visibleData.showAtPosition); + } this._layoutContentWidget(); } @@ -823,10 +853,6 @@ export class ContentHoverWidget extends ResizableContentWidget { public goToBottom(): void { this._hover.scrollbar.setScrollPosition({ scrollTop: this._hover.scrollbar.getScrollDimensions().scrollHeight }); } - - public escape(): void { - this._editor.focus(); - } } export class EditorHoverStatusBar extends Disposable implements IEditorHoverStatusBar { diff --git a/src/vs/editor/contrib/hover/browser/hover.ts b/src/vs/editor/contrib/hover/browser/hover.ts index 881435fe4c9..43969c828b5 100644 --- a/src/vs/editor/contrib/hover/browser/hover.ts +++ b/src/vs/editor/contrib/hover/browser/hover.ts @@ -17,7 +17,7 @@ import { GotoDefinitionAtPositionEditorContribution } from 'vs/editor/contrib/go import { HoverStartMode, HoverStartSource } from 'vs/editor/contrib/hover/browser/hoverOperation'; import { ContentHoverWidget, ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHover'; import { MarginHoverWidget } from 'vs/editor/contrib/hover/browser/marginHover'; -import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { AccessibilitySupport, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -31,6 +31,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver'; import * as nls from 'vs/nls'; import 'vs/css!./hover'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { status } from 'vs/base/browser/ui/aria/aria'; // sticky hover widget which doesn't disappear on focus out and such const _sticky = false @@ -155,6 +157,10 @@ export class ModesHoverController implements IEditorContribution { private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { const target = mouseEvent.target; + if (this._contentWidget?.isFocused || this._contentWidget?.isResizing) { + return; + } + if (this._isMouseDown && this._hoverClicked) { return; } @@ -171,7 +177,7 @@ export class ModesHoverController implements IEditorContribution { if ( !this._isHoverSticky && target.type === MouseTargetType.CONTENT_WIDGET && target.detail === ContentHoverWidget.ID - && this._contentWidget?.isColorPickerVisible() + && this._contentWidget?.isColorPickerVisible ) { // though the hover is not sticky, the color picker needs to. return; @@ -182,7 +188,7 @@ export class ModesHoverController implements IEditorContribution { return; } - if (this._isHoverSticky && this._contentWidget?.isVisibleFromKeyboard()) { + if (this._isHoverSticky && this._contentWidget?.isVisibleFromKeyboard) { // Sticky mode is on and the hover has been shown via keyboard // so moving the mouse has no effect return; @@ -216,9 +222,10 @@ export class ModesHoverController implements IEditorContribution { this._glyphWidget.startShowingAt(target.position.lineNumber); return; } - if (!this._contentWidget?.widget.isResizing && !_sticky) { - this._hideWidgets(); + if (_sticky) { + return; } + this._hideWidgets(); } private _onKeyDown(e: IKeyboardEvent): void { @@ -228,7 +235,7 @@ export class ModesHoverController implements IEditorContribution { const resolvedKeyboardEvent = this._keybindingService.softDispatch(e, this._editor.getDomNode()); // If the beginning of a multi-chord keybinding is pressed, or the command aims to focus the hover, set the variable to true, otherwise false - const mightTriggerFocus = (resolvedKeyboardEvent.kind === ResultKind.MoreChordsNeeded || (resolvedKeyboardEvent.kind === ResultKind.KbFound && resolvedKeyboardEvent.commandId === 'editor.action.showHover' && this._contentWidget?.isVisible())); + const mightTriggerFocus = (resolvedKeyboardEvent.kind === ResultKind.MoreChordsNeeded || (resolvedKeyboardEvent.kind === ResultKind.KbFound && resolvedKeyboardEvent.commandId === 'editor.action.showHover' && this._contentWidget?.isVisible)); if (e.keyCode !== KeyCode.Ctrl && e.keyCode !== KeyCode.Alt && e.keyCode !== KeyCode.Meta && e.keyCode !== KeyCode.Shift && !mightTriggerFocus) { @@ -241,7 +248,7 @@ export class ModesHoverController implements IEditorContribution { if (_sticky) { return; } - if ((this._isMouseDown && this._hoverClicked && this._contentWidget?.isColorPickerVisible()) || InlineSuggestionHintsContentWidget.dropDownVisible) { + if ((this._isMouseDown && this._hoverClicked && this._contentWidget?.isColorPickerVisible) || InlineSuggestionHintsContentWidget.dropDownVisible) { return; } this._hoverActivatedByColorDecoratorClick = false; @@ -257,10 +264,6 @@ export class ModesHoverController implements IEditorContribution { return this._contentWidget; } - public isColorPickerVisible(): boolean { - return this._contentWidget?.isColorPickerVisible() || false; - } - public showContentHover(range: Range, mode: HoverStartMode, source: HoverStartSource, focus: boolean, activatedByColorDecoratorClick: boolean = false): void { this._hoverActivatedByColorDecoratorClick = activatedByColorDecoratorClick; this._getOrCreateContentWidget().startShowingAtRange(range, mode, source, focus); @@ -302,12 +305,12 @@ export class ModesHoverController implements IEditorContribution { this._contentWidget?.goToBottom(); } - public escape(): void { - this._contentWidget?.escape(); + public get isColorPickerVisible(): boolean | undefined { + return this._contentWidget?.isColorPickerVisible; } - public isHoverVisible(): boolean | undefined { - return this._contentWidget?.isVisible(); + public get isHoverVisible(): boolean | undefined { + return this._contentWidget?.isVisible; } public dispose(): void { @@ -360,6 +363,9 @@ class ShowOrFocusHoverAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { + const configurationService = accessor.get(IConfigurationService); + const accessibilityService = accessor.get(IAccessibilityService); + const keybindingService = accessor.get(IKeybindingService); if (!editor.hasModel()) { return; } @@ -371,11 +377,16 @@ class ShowOrFocusHoverAction extends EditorAction { const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); const focus = editor.getOption(EditorOption.accessibilitySupport) === AccessibilitySupport.Enabled || !!args?.focus; - if (controller.isHoverVisible()) { + if (controller.isHoverVisible) { controller.focus(); } else { controller.showContentHover(range, HoverStartMode.Immediate, HoverStartSource.Keyboard, focus); } + if (configurationService.getValue('accessibility.verbosity.hover') && accessibilityService.isScreenReaderOptimized()) { + const keybinding = keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel(); + const hint = keybinding ? nls.localize('chatAccessibleViewHint', "Inspect this in the accessible view with {0}", keybinding) : nls.localize('chatAccessibleViewHintNoKb', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding"); + status(hint); + } } } @@ -665,36 +676,6 @@ class GoToBottomHoverAction extends EditorAction { } } -class EscapeFocusHoverAction extends EditorAction { - - constructor() { - super({ - id: 'editor.action.escapeFocusHover', - label: nls.localize({ - key: 'escapeFocusHover', - comment: [ - 'Action that allows to escape from the hover widget with the escape command when the hover widget is focused.' - ] - }, "Escape Focus Hover"), - alias: 'Escape Focus Hover', - precondition: EditorContextKeys.hoverFocused, - kbOpts: { - kbExpr: EditorContextKeys.hoverFocused, - primary: KeyCode.Escape, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - const controller = ModesHoverController.get(editor); - if (!controller) { - return; - } - controller.escape(); - } -} - registerEditorContribution(ModesHoverController.ID, ModesHoverController, EditorContributionInstantiation.BeforeFirstInteraction); registerEditorAction(ShowOrFocusHoverAction); registerEditorAction(ShowDefinitionPreviewHoverAction); @@ -706,7 +687,6 @@ registerEditorAction(PageUpHoverAction); registerEditorAction(PageDownHoverAction); registerEditorAction(GoToTopHoverAction); registerEditorAction(GoToBottomHoverAction); -registerEditorAction(EscapeFocusHoverAction); HoverParticipantRegistry.register(MarkdownHoverParticipant); HoverParticipantRegistry.register(MarkerHoverParticipant); diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index 93d4104b197..aadc1851a01 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Dimension } from 'vs/base/browser/dom'; import { AsyncIterableObject } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -110,6 +111,10 @@ export interface IEditorHoverRenderContext { * The contents rendered inside the fragment have been changed, which means that the hover should relayout. */ onContentsChanged(): void; + /** + * Set the minimum dimensions of the resizable hover + */ + setMinimumDimensions?(dimensions: Dimension): void; /** * Hide the hover. */ diff --git a/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts b/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts index 69b72483fb3..0243d9e88bf 100644 --- a/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts +++ b/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts @@ -25,13 +25,13 @@ export abstract class ResizableContentWidget extends Disposable implements ICont constructor( protected readonly _editor: ICodeEditor, - initialSize: dom.IDimension = new dom.Dimension(10, 10) + minimumSize: dom.IDimension = new dom.Dimension(10, 10) ) { super(); this._resizableNode.domNode.style.position = 'absolute'; - this._resizableNode.minSize = new dom.Dimension(10, 10); + this._resizableNode.minSize = dom.Dimension.lift(minimumSize); + this._resizableNode.layout(minimumSize.height, minimumSize.width); this._resizableNode.enableSashes(true, true, true, true); - this._resizableNode.layout(initialSize.height, initialSize.width); this._register(this._resizableNode.onDidResize(e => { this._resize(new dom.Dimension(e.dimension.width, e.dimension.height)); if (e.done) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts index fbb7b657c8d..799433568e0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts @@ -11,6 +11,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId, inlineSuggestCommitId } from 'vs/editor/contrib/inlineCompletions/browser/commandIds'; import { InlineCompletionContextKeys } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; +import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; import * as nls from 'vs/nls'; import { MenuId, Action2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -85,6 +86,7 @@ export class AcceptNextWordOfInlineCompletion extends EditorAction { kbOpts: { weight: KeybindingWeight.EditorContrib + 1, primary: KeyMod.CtrlCmd | KeyCode.RightArrow, + kbExpr: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible), }, menuOpts: [{ menuId: MenuId.InlineSuggestionToolbar, @@ -97,7 +99,7 @@ export class AcceptNextWordOfInlineCompletion extends EditorAction { public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { const controller = InlineCompletionsController.get(editor); - controller?.model.get()?.acceptNextWord(controller.editor); + await controller?.model.get()?.acceptNextWord(controller.editor); } } @@ -122,7 +124,7 @@ export class AcceptNextLineOfInlineCompletion extends EditorAction { public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { const controller = InlineCompletionsController.get(editor); - controller?.model.get()?.acceptNextLine(controller.editor); + await controller?.model.get()?.acceptNextLine(controller.editor); } } @@ -145,7 +147,8 @@ export class AcceptInlineCompletion extends EditorAction { kbExpr: ContextKeyExpr.and( InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.tabMovesFocus.toNegated(), - InlineCompletionContextKeys.inlineSuggestionHasIndentationLessThanTabSize + InlineCompletionContextKeys.inlineSuggestionHasIndentationLessThanTabSize, + SuggestContext.Visible.toNegated() ), } }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/ghostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/ghostText.ts index 7c77ba19a3c..abd73b8f0de 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/ghostText.ts @@ -118,6 +118,30 @@ export class GhostTextReplacement { isEmpty(): boolean { return this.parts.every(p => p.lines.length === 0); } + + equals(other: GhostTextReplacement): boolean { + return this.lineNumber === other.lineNumber && + this.columnRange.equals(other.columnRange) && + this.newLines.length === other.newLines.length && + this.newLines.every((line, index) => line === other.newLines[index]) && + this.additionalReservedLineCount === other.additionalReservedLineCount; + } } export type GhostTextOrReplacement = GhostText | GhostTextReplacement; + +export function ghostTextOrReplacementEquals(a: GhostTextOrReplacement | undefined, b: GhostTextOrReplacement | undefined): boolean { + if (a === b) { + return true; + } + if (!a || !b) { + return false; + } + if (a instanceof GhostText && b instanceof GhostText) { + return a.equals(b); + } + if (a instanceof GhostTextReplacement && b instanceof GhostTextReplacement) { + return a.equals(b); + } + return false; +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts index e2c2fa9d796..a43c994c249 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts @@ -46,7 +46,8 @@ export class GhostTextWidget extends Disposable { this._register(applyObservableDecorations(this.editor, this.decorations)); } - private readonly uiState = derived('uiState', reader => { + private readonly uiState = derived(reader => { + /** @description uiState */ if (this.isDisposed.read(reader)) { return undefined; } @@ -125,7 +126,8 @@ export class GhostTextWidget extends Disposable { }; }); - private readonly decorations = derived('decorations', reader => { + private readonly decorations = derived(reader => { + /** @description decorations */ const uiState = this.uiState.read(reader); if (!uiState) { return []; @@ -165,7 +167,8 @@ export class GhostTextWidget extends Disposable { new AdditionalLinesWidget( this.editor, this.languageService.languageIdCodec, - derived('lines', (reader) => { + derived(reader => { + /** @description lines */ const uiState = this.uiState.read(reader); return uiState ? { lineNumber: uiState.lineNumber, @@ -204,7 +207,8 @@ class AdditionalLinesWidget extends Disposable { ) { super(); - this._register(autorun('update view zone', reader => { + this._register(autorun(reader => { + /** @description update view zone */ const lines = this.lines.read(reader); this.editorOptionsChanged.read(reader); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts index efa99199a6a..a5f143ee42d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts @@ -103,7 +103,7 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan comment: 'This event tracks whenever an inline completion hover is shown.'; }>('inlineCompletionHover.shown'); - if (this.accessibilityService.isScreenReaderOptimized()) { + if (this.accessibilityService.isScreenReaderOptimized() && !this._editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { this.renderScreenReaderText(context, part, disposableStore); } @@ -139,7 +139,8 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan hoverContentsElement.replaceChildren(renderedContents.element); }; - disposableStore.add(autorun('update hover', (reader) => { + disposableStore.add(autorun(reader => { + /** @description update hover */ const ghostText = part.controller.model.read(reader)?.ghostText.read(reader); if (ghostText) { const lineText = this._editor.getModel()!.getLineContent(ghostText.lineNumber); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts index bcd18d70301..1e9a7fbbbd0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts @@ -28,19 +28,21 @@ export class InlineCompletionContextKeys extends Disposable { ) { super(); - this._register(autorun('update context key: inlineCompletionVisible, suppressSuggestions', (reader) => { + this._register(autorun(reader => { + /** @description update context key: inlineCompletionVisible, suppressSuggestions */ const model = this.model.read(reader); - const suggestion = model?.selectedInlineCompletion.read(reader); - const ghostText = model?.ghostText.read(reader); - const selectedSuggestItem = model?.selectedSuggestItem.read(reader); - this.inlineCompletionVisible.set(selectedSuggestItem === undefined && ghostText !== undefined && !ghostText.isEmpty()); + const state = model?.state.read(reader); - if (ghostText && suggestion) { - this.suppressSuggestions.set(suggestion.inlineCompletion.source.inlineCompletions.suppressSuggestions); + const isInlineCompletionVisible = !!state?.inlineCompletion && state?.ghostText !== undefined && !state?.ghostText.isEmpty(); + this.inlineCompletionVisible.set(isInlineCompletionVisible); + + if (state?.ghostText && state?.inlineCompletion) { + this.suppressSuggestions.set(state.inlineCompletion.inlineCompletion.source.inlineCompletions.suppressSuggestions); } })); - this._register(autorun('update context key: inlineCompletionSuggestsIndentation, inlineCompletionSuggestsIndentationLessThanTabSize', (reader) => { + this._register(autorun(reader => { + /** @description update context key: inlineCompletionSuggestsIndentation, inlineCompletionSuggestsIndentationLessThanTabSize */ const model = this.model.read(reader); let startsWithIndentation = false; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 910f4b678eb..1e5a0bb0f8d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -6,8 +6,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { autorun, constObservable, observableFromEvent, observableValue } from 'vs/base/common/observable'; -import { ITransaction, disposableObservableValue, transaction } from 'vs/base/common/observableImpl/base'; +import { ITransaction, autorun, constObservable, disposableObservableValue, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { CoreEditingCommands } from 'vs/editor/browser/coreCommands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -154,7 +153,8 @@ export class InlineCompletionsController extends Disposable { }); })); - this._register(autorun('forceRenderingAbove', reader => { + this._register(autorun(reader => { + /** @description forceRenderingAbove */ const state = this.model.read(reader)?.state.read(reader); if (state?.suggestItem) { if (state.ghostText.lineCount >= 2) { @@ -169,23 +169,20 @@ export class InlineCompletionsController extends Disposable { })); let lastInlineCompletionId: string | undefined = undefined; - this._register(autorun('play audio cue & read suggestion', reader => { + this._register(autorun(reader => { + /** @description play audio cue & read suggestion */ const model = this.model.read(reader); const state = model?.state.read(reader); - if (!model || !state || !state.completion) { + if (!model || !state || !state.inlineCompletion) { lastInlineCompletionId = undefined; return; } - if (state.completion.semanticId !== lastInlineCompletionId) { - lastInlineCompletionId = state.completion.semanticId; - if (model.isNavigatingCurrentInlineCompletion) { - return; - } - + if (state.inlineCompletion.semanticId !== lastInlineCompletionId) { + lastInlineCompletionId = state.inlineCompletion.semanticId; + const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => { if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { - const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); alert(state.ghostText.renderForScreenReader(lineText)); } }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts index c1c4376977b..eb9aa10b05c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts @@ -11,8 +11,7 @@ import { equals } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, derived, observableFromEvent } from 'vs/base/common/observable'; -import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; +import { IObservable, autorun, autorunWithStore, derived, observableFromEvent } from 'vs/base/common/observable'; import { OS } from 'vs/base/common/platform'; import { ThemeIcon } from 'vs/base/common/themables'; import 'vs/css!./inlineCompletionsHintsWidget'; @@ -21,10 +20,10 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Command, InlineCompletionTriggerKind } from 'vs/editor/common/languages'; import { PositionAffinity } from 'vs/editor/common/model'; -import { showPreviousInlineSuggestionActionId, showNextInlineSuggestionActionId } from 'vs/editor/contrib/inlineCompletions/browser/commandIds'; +import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId } from 'vs/editor/contrib/inlineCompletions/browser/commandIds'; import { InlineCompletionsModel } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel'; import { localize } from 'vs/nls'; -import { createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -40,7 +39,8 @@ export class InlineCompletionsHintsWidget extends Disposable { private sessionPosition: Position | undefined = undefined; - private readonly position = derived('position', reader => { + private readonly position = derived(reader => { + /** @description position */ const ghostText = this.model.read(reader)?.ghostText.read(reader); if (!this.alwaysShowToolbar.read(reader) || !ghostText || ghostText.parts.length === 0) { @@ -65,7 +65,8 @@ export class InlineCompletionsHintsWidget extends Disposable { ) { super(); - this._register(autorunWithStore2('setup content widget', (reader, store) => { + this._register(autorunWithStore((reader, store) => { + /** @description setup content widget */ const model = this.model.read(reader); if (!model || !this.alwaysShowToolbar.read(reader)) { return; @@ -83,7 +84,8 @@ export class InlineCompletionsHintsWidget extends Disposable { editor.addContentWidget(contentWidget); store.add(toDisposable(() => editor.removeContentWidget(contentWidget))); - store.add(autorun('request explicit', reader => { + store.add(autorun(reader => { + /** @description request explicit */ const position = this.position.read(reader); if (!position) { return; @@ -190,12 +192,14 @@ export class InlineSuggestionHintsContentWidget extends Disposable implements IC InlineSuggestionHintsContentWidget._dropDownVisible = e; })); - this._register(autorun('update position', (reader) => { + this._register(autorun(reader => { + /** @description update position */ this._position.read(reader); this.editor.layoutContentWidget(this); })); - this._register(autorun('counts', (reader) => { + this._register(autorun(reader => { + /** @description counts */ const suggestionCount = this._suggestionCount.read(reader); const currentSuggestionIdx = this._currentSuggestionIdx.read(reader); @@ -214,7 +218,8 @@ export class InlineSuggestionHintsContentWidget extends Disposable implements IC } })); - this._register(autorun('extra commands', (reader) => { + this._register(autorun(reader => { + /** @description extra commands */ const extraCommands = this._extraCommands.read(reader); if (equals(this.lastCommands, extraCommands)) { // nothing to update diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 95346e787b6..310143f4563 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -6,9 +6,7 @@ import { mapFind } from 'vs/base/common/arrays'; import { BugIndicatingError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, ITransaction, autorun, derived, keepAlive, observableSignal, observableValue, transaction } from 'vs/base/common/observable'; -import { subtransaction } from 'vs/base/common/observableImpl/base'; -import { derivedHandleChanges } from 'vs/base/common/observableImpl/derived'; +import { IObservable, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, keepAlive, observableSignal, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -18,7 +16,7 @@ import { InlineCompletionContext, InlineCompletionTriggerKind } from 'vs/editor/ import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model'; import { IFeatureDebounceInformation } from 'vs/editor/common/services/languageFeatureDebounce'; -import { GhostText, GhostTextOrReplacement } from 'vs/editor/contrib/inlineCompletions/browser/ghostText'; +import { GhostText, GhostTextOrReplacement, ghostTextOrReplacementEquals } from 'vs/editor/contrib/inlineCompletions/browser/ghostText'; import { InlineCompletionWithUpdatedRange, InlineCompletionsSource } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource'; import { SuggestItemInfo } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider'; import { addPositions, lengthOfText } from 'vs/editor/contrib/inlineCompletions/browser/utils'; @@ -44,9 +42,6 @@ export class InlineCompletionsModel extends Disposable { private _isAcceptingPartially = false; public get isAcceptingPartially() { return this._isAcceptingPartially; } - private _isNavigatingCurrentInlineCompletion = false; - public get isNavigatingCurrentInlineCompletion() { return this._isNavigatingCurrentInlineCompletion; } - constructor( public readonly textModel: ITextModel, public readonly selectedSuggestItem: IObservable, @@ -66,9 +61,10 @@ export class InlineCompletionsModel extends Disposable { this._register(keepAlive(this._fetchInlineCompletions, true)); let lastItem: InlineCompletionWithUpdatedRange | undefined = undefined; - this._register(autorun('call handleItemDidShow', reader => { + this._register(autorun(reader => { + /** @description call handleItemDidShow */ const item = this.state.read(reader); - const completion = item?.completion; + const completion = item?.inlineCompletion; if (completion?.semanticId !== lastItem?.semanticId) { lastItem = completion; if (completion) { @@ -153,7 +149,8 @@ export class InlineCompletionsModel extends Disposable { }); } - private readonly _filteredInlineCompletionItems = derived('filteredInlineCompletionItems', (reader) => { + private readonly _filteredInlineCompletionItems = derived(reader => { + /** @description _filteredInlineCompletionItems */ const c = this._source.inlineCompletions.read(reader); if (!c) { return []; } const cursorPosition = this.cursorPosition.read(reader); @@ -161,7 +158,8 @@ export class InlineCompletionsModel extends Disposable { return filteredCompletions; }); - public readonly selectedInlineCompletionIndex = derived('selectedCachedCompletionIndex', (reader) => { + public readonly selectedInlineCompletionIndex = derived((reader) => { + /** @description selectedInlineCompletionIndex */ const selectedInlineCompletionId = this._selectedInlineCompletionId.read(reader); const filteredCompletions = this._filteredInlineCompletionItems.read(reader); const idx = this._selectedInlineCompletionId === undefined ? -1 @@ -174,7 +172,8 @@ export class InlineCompletionsModel extends Disposable { return idx; }); - public readonly selectedInlineCompletion = derived('selectedCachedCompletion', (reader) => { + public readonly selectedInlineCompletion = derived((reader) => { + /** @description selectedCachedCompletion */ const filteredCompletions = this._filteredInlineCompletionItems.read(reader); const idx = this.selectedInlineCompletionIndex.read(reader); return filteredCompletions[idx]; @@ -184,7 +183,8 @@ export class InlineCompletionsModel extends Disposable { v => /** @description lastTriggerKind */ v?.request.context.triggerKind ); - public readonly inlineCompletionsCount = derived('selectedInlineCompletionsCount', reader => { + public readonly inlineCompletionsCount = derived(reader => { + /** @description inlineCompletionsCount */ if (this.lastTriggerKind.read(reader) === InlineCompletionTriggerKind.Explicit) { return this._filteredInlineCompletionItems.read(reader).length; } else { @@ -192,11 +192,19 @@ export class InlineCompletionsModel extends Disposable { } }); - public readonly state = derived<{ + public readonly state = derivedOpts<{ suggestItem: SuggestItemInfo | undefined; - completion: InlineCompletionWithUpdatedRange | undefined; + inlineCompletion: InlineCompletionWithUpdatedRange | undefined; ghostText: GhostTextOrReplacement; - } | undefined>('ghostTextAndCompletion', (reader) => { + } | undefined>({ + equalityComparer: (a, b) => { + if (!a || !b) { return a === b; } + return ghostTextOrReplacementEquals(a.ghostText, b.ghostText) + && a.inlineCompletion === b.inlineCompletion + && a.suggestItem === b.suggestItem; + } + }, (reader) => { + /** @description ghostTextAndCompletion */ const model = this.textModel; const suggestItem = this.selectedSuggestItem.read(reader); @@ -228,7 +236,7 @@ export class InlineCompletionsModel extends Disposable { // Show an invisible ghost text to reserve space const ghostText = newGhostText ?? new GhostText(edit.range.endLineNumber, []); - return { ghostText, completion: augmentedCompletion?.completion, suggestItem }; + return { ghostText, inlineCompletion: augmentedCompletion?.completion, suggestItem }; } else { if (!this._isActive.read(reader)) { return undefined; } const item = this.selectedInlineCompletion.read(reader); @@ -238,11 +246,14 @@ export class InlineCompletionsModel extends Disposable { const mode = this._inlineSuggestMode.read(reader); const cursor = this.cursorPosition.read(reader); const ghostText = replacement.computeGhostText(model, mode, cursor); - return ghostText ? { ghostText, completion: item, suggestItem: undefined } : undefined; + return ghostText ? { ghostText, inlineCompletion: item, suggestItem: undefined } : undefined; } }); - public readonly ghostText = derived('ghostText', (reader) => { + public readonly ghostText = derivedOpts({ + equalityComparer: ghostTextOrReplacementEquals + }, reader => { + /** @description ghostText */ const v = this.state.read(reader); if (!v) { return undefined; } return v.ghostText; @@ -251,17 +262,12 @@ export class InlineCompletionsModel extends Disposable { private async _deltaSelectedInlineCompletionIndex(delta: 1 | -1): Promise { await this.triggerExplicitly(); - this._isNavigatingCurrentInlineCompletion = true; - try { - const completions = this._filteredInlineCompletionItems.get() || []; - if (completions.length > 0) { - const newIdx = (this.selectedInlineCompletionIndex.get() + delta + completions.length) % completions.length; - this._selectedInlineCompletionId.set(completions[newIdx].semanticId, undefined); - } else { - this._selectedInlineCompletionId.set(undefined, undefined); - } - } finally { - this._isNavigatingCurrentInlineCompletion = false; + const completions = this._filteredInlineCompletionItems.get() || []; + if (completions.length > 0) { + const newIdx = (this.selectedInlineCompletionIndex.get() + delta + completions.length) % completions.length; + this._selectedInlineCompletionId.set(completions[newIdx].semanticId, undefined); + } else { + this._selectedInlineCompletionId.set(undefined, undefined); } } @@ -278,11 +284,11 @@ export class InlineCompletionsModel extends Disposable { throw new BugIndicatingError(); } - const ghostText = this.ghostText.get(); - const completion = this.selectedInlineCompletion.get()?.toInlineCompletion(undefined); - if (!ghostText || !completion) { + const state = this.state.get(); + if (!state || state.ghostText.isEmpty() || !state.inlineCompletion) { return; } + const completion = state.inlineCompletion.toInlineCompletion(undefined); editor.pushUndoStop(); if (completion.snippetInfo) { @@ -306,20 +312,28 @@ export class InlineCompletionsModel extends Disposable { } if (completion.command) { - await this._commandService - .executeCommand(completion.command.id, ...(completion.command.arguments || [])) - .then(undefined, onUnexpectedExternalError); + // Make sure the completion list will not be disposed. + completion.source.addRef(); } + + // Reset before invoking the command, since the command might cause a follow up trigger. transaction(tx => { this._source.clear(tx); // Potentially, isActive will get set back to true by the typing or accept inline suggest event // if automatic inline suggestions are enabled. this._isActive.set(false, tx); }); + + if (completion.command) { + await this._commandService + .executeCommand(completion.command.id, ...(completion.command.arguments || [])) + .then(undefined, onUnexpectedExternalError); + completion.source.removeRef(); + } } - public acceptNextWord(editor: ICodeEditor): void { - this._acceptNext(editor, (pos, text) => { + public async acceptNextWord(editor: ICodeEditor): Promise { + await this._acceptNext(editor, (pos, text) => { const langId = this.textModel.getLanguageIdAtPosition(pos.lineNumber, pos.column); const config = this._languageConfigurationService.getLanguageConfiguration(langId); const wordRegExp = new RegExp(config.wordDefinition.source, config.wordDefinition.flags.replace('g', '')); @@ -347,8 +361,8 @@ export class InlineCompletionsModel extends Disposable { }); } - public acceptNextLine(editor: ICodeEditor): void { - this._acceptNext(editor, (pos, text) => { + public async acceptNextLine(editor: ICodeEditor): Promise { + await this._acceptNext(editor, (pos, text) => { const m = text.match(/\n/); if (m && m.index !== undefined) { return m.index + 1; @@ -357,26 +371,24 @@ export class InlineCompletionsModel extends Disposable { }); } - private _acceptNext(editor: ICodeEditor, getAcceptUntilIndex: (position: Position, text: string) => number): void { + private async _acceptNext(editor: ICodeEditor, getAcceptUntilIndex: (position: Position, text: string) => number): Promise { if (editor.getModel() !== this.textModel) { throw new BugIndicatingError(); } - const ghostText = this.ghostText.get(); - const completion = this.selectedInlineCompletion.get()?.toInlineCompletion(undefined); - if (!ghostText || !completion) { + const state = this.state.get(); + if (!state || state.ghostText.isEmpty() || !state.inlineCompletion) { return; } + const ghostText = state.ghostText; + const completion = state.inlineCompletion.toInlineCompletion(undefined); if (completion.snippetInfo || completion.filterText !== completion.insertText) { // not in WYSIWYG mode, partial commit might change completion, thus it is not supported - this.accept(editor); + await this.accept(editor); return; } - if (ghostText.parts.length === 0) { - return; - } const firstPart = ghostText.parts[0]; const position = new Position(ghostText.lineNumber, firstPart.column); const line = firstPart.lines.join('\n'); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts index c844e63e8a1..14a7cdf1f44 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts @@ -6,8 +6,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { matchesSubString } from 'vs/base/common/filters'; import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { ITransaction, derived } from 'vs/base/common/observable'; -import { IObservable, IReader, disposableObservableValue, transaction } from 'vs/base/common/observableImpl/base'; +import { IObservable, IReader, ITransaction, derived, disposableObservableValue, transaction } from 'vs/base/common/observable'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { InlineCompletionContext, InlineCompletionTriggerKind } from 'vs/editor/common/languages'; @@ -15,8 +14,8 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model'; import { IFeatureDebounceInformation } from 'vs/editor/common/services/languageFeatureDebounce'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { SingleTextEdit } from 'vs/editor/contrib/inlineCompletions/browser/singleTextEdit'; import { InlineCompletionItem, InlineCompletionProviderResult, provideInlineCompletions } from 'vs/editor/contrib/inlineCompletions/browser/provideInlineCompletions'; +import { SingleTextEdit } from 'vs/editor/contrib/inlineCompletions/browser/singleTextEdit'; export class InlineCompletionsSource extends Disposable { private readonly _updateOperation = this._register(new MutableDisposable()); @@ -91,6 +90,7 @@ export class InlineCompletionsSource extends Disposable { this._updateOperation.clear(); transaction(tx => { + /** @description Update completions with provider result */ target.set(completions, tx); }); @@ -183,7 +183,8 @@ export class UpToDateInlineCompletions implements IDisposable { private readonly _prependedInlineCompletionItems: InlineCompletionItem[] = []; private _rangeVersionIdValue = 0; - private readonly _rangeVersionId = derived('ranges', reader => { + private readonly _rangeVersionId = derived(reader => { + /** @description ranges */ this.versionId.read(reader); let changed = false; for (const i of this._inlineCompletions) { @@ -221,7 +222,13 @@ export class UpToDateInlineCompletions implements IDisposable { public dispose(): void { this._refCount--; if (this._refCount === 0) { - this.textModel.deltaDecorations(this._inlineCompletions.map(i => i.decorationId), []); + setTimeout(() => { + // To fix https://github.com/microsoft/vscode/issues/188348 + if (!this.textModel.isDisposed()) { + // This is just cleanup. It's ok if it happens with a delay. + this.textModel.deltaDecorations(this._inlineCompletions.map(i => i.decorationId), []); + } + }, 0); this.inlineCompletionProviderResult.dispose(); for (const i of this._prependedInlineCompletionItems) { i.source.removeRef(); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/provideInlineCompletions.ts index 5373751efa9..a9952a50f14 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/provideInlineCompletions.ts @@ -4,14 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { assertNever } from 'vs/base/common/assert'; +import { DeferredPromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { SetMap } from 'vs/base/common/collections'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; -import { Command, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider } from 'vs/editor/common/languages'; +import { Command, InlineCompletion, InlineCompletionContext, InlineCompletionProviderGroupId, InlineCompletions, InlineCompletionsProvider } from 'vs/editor/common/languages'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ITextModel } from 'vs/editor/common/model'; import { fixBracketsInLine } from 'vs/editor/common/model/bracketPairsTextModelPart/fixBrackets'; @@ -29,17 +31,87 @@ export async function provideInlineCompletions( ): Promise { // Important: Don't use position after the await calls, as the model could have been changed in the meantime! const defaultReplaceRange = getDefaultRange(position, model); - const providers = registry.all(model); - const providerResults = await Promise.all(providers.map(async provider => { - try { - const completions = await provider.provideInlineCompletions(model, position, context, token); - return ({ provider, completions }); - } catch (e) { - onUnexpectedExternalError(e); + + const multiMap = new SetMap>(); + for (const provider of providers) { + if (provider.groupId) { + multiMap.add(provider.groupId, provider); } - return ({ provider, completions: undefined }); - })); + } + + function getPreferredProviders(provider: InlineCompletionsProvider): InlineCompletionsProvider[] { + if (!provider.yieldsToGroupIds) { return []; } + const result: InlineCompletionsProvider[] = []; + for (const groupId of provider.yieldsToGroupIds || []) { + const providers = multiMap.get(groupId); + for (const p of providers) { + result.push(p); + } + } + return result; + } + + type Result = Promise | null | undefined>; + const states = new Map>, Result>(); + + const seen = new Set>>(); + function findPreferredProviderCircle(provider: InlineCompletionsProvider, stack: InlineCompletionsProvider[]): InlineCompletionsProvider[] | undefined { + stack = [...stack, provider]; + if (seen.has(provider)) { return stack; } + + seen.add(provider); + try { + const preferred = getPreferredProviders(provider); + for (const p of preferred) { + const c = findPreferredProviderCircle(p, stack); + if (c) { return c; } + } + } finally { + seen.delete(provider); + } + return undefined; + } + + function processProvider(provider: InlineCompletionsProvider): Result { + const state = states.get(provider); + if (state) { + return state; + } + + const circle = findPreferredProviderCircle(provider, []); + if (circle) { + onUnexpectedExternalError(new Error(`Inline completions: cyclic yield-to dependency detected. Path: ${circle.map(s => s.toString ? s.toString() : ('' + s)).join(' -> ')}`)); + } + + const deferredPromise = new DeferredPromise | null | undefined>(); + states.set(provider, deferredPromise.p); + + (async () => { + if (!circle) { + const preferred = getPreferredProviders(provider); + for (const p of preferred) { + const result = await processProvider(p); + if (result && result.items.length > 0) { + // Skip provider + return undefined; + } + } + } + + try { + const completions = await provider.provideInlineCompletions(model, position, context, token); + return completions; + } catch (e) { + onUnexpectedExternalError(e); + return undefined; + } + })().then(c => deferredPromise.complete(c), e => deferredPromise.error(e)); + + return deferredPromise.p; + } + + const providerResults = await Promise.all(providers.map(async provider => ({ provider, completions: await processProvider(provider) }))); const itemsByHash = new Map(); const lists: InlineCompletionList[] = []; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts index b2e56cdea96..1301ebc410a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts @@ -123,6 +123,7 @@ export class SuggestWidgetAdaptor extends Disposable { this._currentSuggestItemInfo = newInlineCompletion; transaction(tx => { + /** @description Update state from suggest widget */ this.checkModelVersion(tx); this._selectedItem.set(this._isActive ? this._currentSuggestItemInfo : undefined, tx); }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts index b256d9dc8d7..a1876c8a6fc 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts @@ -5,7 +5,7 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun } from 'vs/base/common/observable'; +import { IObservable, autorunOpts } from 'vs/base/common/observable'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -67,12 +67,17 @@ export class ColumnRange { toRange(lineNumber: number): Range { return new Range(lineNumber, this.startColumn, lineNumber, this.endColumnExclusive); } + + equals(other: ColumnRange): boolean { + return this.startColumn === other.startColumn + && this.endColumnExclusive === other.endColumnExclusive; + } } export function applyObservableDecorations(editor: ICodeEditor, decorations: IObservable): IDisposable { const d = new DisposableStore(); const decorationsCollection = editor.createDecorationsCollection(); - d.add(autorun(`Apply decorations from ${decorations.debugName}`, reader => { + d.add(autorunOpts({ debugName: () => `Apply decorations from ${decorations.debugName}` }, reader => { const d = decorations.read(reader); decorationsCollection.set(d); })); diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts index 661894710d7..e46c38499f1 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -50,7 +50,8 @@ suite('Suggest Widget Model', () => { async ({ editor, editorViewModel, context, model }) => { let last: boolean | undefined = undefined; const history = new Array(); - const d = autorun('debug', reader => { + const d = autorun(reader => { + /** @description debug */ const selectedSuggestItem = !!model.selectedSuggestItem.read(reader); if (last !== selectedSuggestItem) { last = selectedSuggestItem; diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts index b25536d3703..c3c25e22f9c 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts @@ -80,7 +80,8 @@ export class GhostTextContext extends Disposable { constructor(model: InlineCompletionsModel, private readonly editor: ITestCodeEditor) { super(); - this._register(autorun('update', reader => { + this._register(autorun(reader => { + /** @description update */ const ghostText = model.ghostText.read(reader); let view: string | undefined; if (ghostText) { diff --git a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts index dffb158ed25..e19fc94e97c 100644 --- a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts @@ -86,7 +86,8 @@ suite('Multicursor selection', () => { const serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { _serviceBrand: undefined, - onDidChangeValue: Event.None, + onDidChangeValue: () => { throw new Error(); }, + onDidChangeValue2: Event.None, onDidChangeTarget: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], diff --git a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts index d6c3feb2a31..77197c2f9d4 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts @@ -14,7 +14,7 @@ import { format, trim } from 'vs/base/common/strings'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { DocumentSymbol, SymbolKind, SymbolKinds, SymbolTag } from 'vs/editor/common/languages'; +import { DocumentSymbol, SymbolKind, SymbolKinds, SymbolTag, getAriaLabelForSymbol } from 'vs/editor/common/languages'; import { IOutlineModelService } from 'vs/editor/contrib/documentSymbols/browser/outlineModel'; import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions, IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess'; import { localize } from 'vs/nls'; @@ -316,7 +316,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit kind: symbol.kind, score: symbolScore, label: symbolLabelWithIcon, - ariaLabel: symbolLabel, + ariaLabel: getAriaLabelForSymbol(symbol.name, symbol.kind), description: containerLabel, highlights: deprecated ? undefined : { label: symbolMatches, diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index 7d7103cd477..87d27b48d5c 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { assertType } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, EditorContributionInstantiation, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -188,10 +188,10 @@ export class SnippetController2 implements IEditorContribution { const model = this._editor.getModel(); - let registration: IDisposable = Disposable.None; + let registration: IDisposable | undefined; let isRegistered = false; const disable = () => { - registration.dispose(); + registration?.dispose(); isRegistered = false; }; @@ -203,11 +203,11 @@ export class SnippetController2 implements IEditorContribution { scheme: model.uri.scheme, exclusive: true }, provider); + this._snippetListener.add(registration); isRegistered = true; } }; - this._snippetListener.add(registration); this._choiceCompletions = { provider, enable, disable }; } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index fb7adfb064a..132fb4ff46c 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -26,6 +26,7 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua import { ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import * as dom from 'vs/base/browser/dom'; import { StickyRange } from 'vs/editor/contrib/stickyScroll/browser/stickyScrollElement'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; interface CustomMouseEvent { detail: string; @@ -290,7 +291,9 @@ export class StickyScrollController extends Disposable implements IEditorContrib return linkGestureStore; } - private _onContextMenu(event: MouseEvent) { + private _onContextMenu(e: MouseEvent) { + const event = new StandardMouseEvent(e); + this._contextMenuService.showContextMenu({ menuId: MenuId.StickyScrollContext, getAnchor: () => event, diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index d2e86783c8a..8cbfc6ef9cb 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -237,9 +237,9 @@ export class SuggestWidget implements IDisposable { if (typeof item.completion.label !== 'string') { const { detail, description } = item.completion.label; if (detail && description) { - label = nls.localize('label.full', '{0}{1}, {2}', label, detail, description); + label = nls.localize('label.full', '{0} {1}, {2}', label, detail, description); } else if (detail) { - label = nls.localize('label.detail', '{0}{1}', label, detail); + label = nls.localize('label.detail', '{0} {1}', label, detail); } else if (description) { label = nls.localize('label.desc', '{0}, {1}', label, description); } diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index 9b77b25b72e..eea1de6f65b 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -429,6 +429,7 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa } const result: MarkdownHover[] = []; + const existedReason = new Set(); let index = 300; for (const d of lineDecorations) { @@ -480,6 +481,11 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa break; } + if (existedReason.has(reason)) { + continue; + } + existedReason.add(reason); + const adjustSettingsArgs: ShowExcludeOptionsArgs = { codePoint: codePoint, reason: highlightInfo.reason, diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 43e1f1eef10..d9a8d83cf09 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -38,6 +38,7 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; /** * Description of an action contribution @@ -572,7 +573,8 @@ export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandal @IConfigurationService configurationService: IConfigurationService, @IContextMenuService contextMenuService: IContextMenuService, @IEditorProgressService editorProgressService: IEditorProgressService, - @IClipboardService clipboardService: IClipboardService + @IClipboardService clipboardService: IClipboardService, + @IAudioCueService audioCueService: IAudioCueService, ) { const options = { ..._options }; updateConfigurationService(configurationService, options, true); @@ -591,6 +593,7 @@ export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandal contextKeyService, instantiationService, codeEditorService, + audioCueService, ); this._configurationService = configurationService; diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 78d683d2fe5..939d3391034 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -54,16 +54,18 @@ export function getEncodedLanguageId(languageId: string): number { * @event */ export function onLanguage(languageId: string, callback: () => void): IDisposable { - const languageService = StandaloneServices.get(ILanguageService); - const disposable = languageService.onDidRequestRichLanguageFeatures((encounteredLanguageId) => { - if (encounteredLanguageId === languageId) { - // stop listening - disposable.dispose(); - // invoke actual listener - callback(); - } + return StandaloneServices.withServices(() => { + const languageService = StandaloneServices.get(ILanguageService); + const disposable = languageService.onDidRequestRichLanguageFeatures((encounteredLanguageId) => { + if (encounteredLanguageId === languageId) { + // stop listening + disposable.dispose(); + // invoke actual listener + callback(); + } + }); + return disposable; }); - return disposable; } /** @@ -72,16 +74,18 @@ export function onLanguage(languageId: string, callback: () => void): IDisposabl * @event */ export function onLanguageEncountered(languageId: string, callback: () => void): IDisposable { - const languageService = StandaloneServices.get(ILanguageService); - const disposable = languageService.onDidRequestBasicLanguageFeatures((encounteredLanguageId) => { - if (encounteredLanguageId === languageId) { - // stop listening - disposable.dispose(); - // invoke actual listener - callback(); - } + return StandaloneServices.withServices(() => { + const languageService = StandaloneServices.get(ILanguageService); + const disposable = languageService.onDidRequestBasicLanguageFeatures((encounteredLanguageId) => { + if (encounteredLanguageId === languageId) { + // stop listening + disposable.dispose(); + // invoke actual listener + callback(); + } + }); + return disposable; }); - return disposable; } /** diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index cc2ede5219c..f026f61f400 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -87,7 +87,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { DefaultConfiguration } from 'vs/platform/configuration/common/configurations'; import { WorkspaceEdit } from 'vs/editor/common/languages'; -import { AudioCue, AudioCueGroupId, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService'; +import { AudioCue, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService'; import { LogService } from 'vs/platform/log/common/logService'; import { getEditorFeatures } from 'vs/editor/common/editorFeatures'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -1058,8 +1058,6 @@ class StandaloneAudioService implements IAudioCueService { playAudioCueLoop(cue: AudioCue): IDisposable { return toDisposable(() => { }); } - playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void { - } } export interface IEditorOverrideServices { @@ -1128,6 +1126,7 @@ export module StandaloneServices { } let initialized = false; + const onDidInitialize = new Emitter(); export function initialize(overrides: IEditorOverrideServices): IInstantiationService { if (initialized) { return instantiationService; @@ -1163,6 +1162,27 @@ export module StandaloneServices { } } + onDidInitialize.fire(); + return instantiationService; } + + /** + * Executes callback once services are initialized. + */ + export function withServices(callback: () => IDisposable): IDisposable { + if (initialized) { + return callback(); + } + + const disposable = new DisposableStore(); + + const listener = disposable.add(onDidInitialize.event(() => { + listener.dispose(); + disposable.add(callback()); + })); + + return disposable; + } + } diff --git a/src/vs/editor/standalone/browser/standaloneThemeService.ts b/src/vs/editor/standalone/browser/standaloneThemeService.ts index ec49af70ece..a391249e6e0 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeService.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeService.ts @@ -392,7 +392,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe colorVariables.push(`${asCssVariableName(item.id)}: ${color.toString()};`); } } - ruleCollector.addRule(`.monaco-editor { ${colorVariables.join('\n')} }`); + ruleCollector.addRule(`.monaco-editor, .monaco-diff-editor { ${colorVariables.join('\n')} }`); const colorMap = this._colorMapOverride || this._theme.tokenTheme.getColorMap(); ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); diff --git a/src/vs/editor/standalone/common/themes.ts b/src/vs/editor/standalone/common/themes.ts index e283e315d2c..e5f9b91bf22 100644 --- a/src/vs/editor/standalone/common/themes.ts +++ b/src/vs/editor/standalone/common/themes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { editorActiveIndentGuides, editorIndentGuides } from 'vs/editor/common/core/editorColorRegistry'; +import { editorActiveIndentGuide1, editorIndentGuide1 } from 'vs/editor/common/core/editorColorRegistry'; import { IStandaloneThemeData } from 'vs/editor/standalone/common/standaloneTheme'; import { editorBackground, editorForeground, editorInactiveSelection, editorSelectionHighlight } from 'vs/platform/theme/common/colorRegistry'; @@ -71,8 +71,8 @@ export const vs: IStandaloneThemeData = { [editorBackground]: '#FFFFFE', [editorForeground]: '#000000', [editorInactiveSelection]: '#E5EBF1', - [editorIndentGuides]: '#D3D3D3', - [editorActiveIndentGuides]: '#939393', + [editorIndentGuide1]: '#D3D3D3', + [editorActiveIndentGuide1]: '#939393', [editorSelectionHighlight]: '#ADD6FF4D' } }; @@ -142,8 +142,8 @@ export const vs_dark: IStandaloneThemeData = { [editorBackground]: '#1E1E1E', [editorForeground]: '#D4D4D4', [editorInactiveSelection]: '#3A3D41', - [editorIndentGuides]: '#404040', - [editorActiveIndentGuides]: '#707070', + [editorIndentGuide1]: '#404040', + [editorActiveIndentGuide1]: '#707070', [editorSelectionHighlight]: '#ADD6FF26' } }; @@ -204,8 +204,8 @@ export const hc_black: IStandaloneThemeData = { colors: { [editorBackground]: '#000000', [editorForeground]: '#FFFFFF', - [editorIndentGuides]: '#FFFFFF', - [editorActiveIndentGuides]: '#FFFFFF', + [editorIndentGuide1]: '#FFFFFF', + [editorActiveIndentGuide1]: '#FFFFFF', } }; /* -------------------------------- End hc-black theme -------------------------------- */ @@ -263,8 +263,8 @@ export const hc_light: IStandaloneThemeData = { colors: { [editorBackground]: '#FFFFFF', [editorForeground]: '#292929', - [editorIndentGuides]: '#292929', - [editorActiveIndentGuides]: '#292929', + [editorIndentGuide1]: '#292929', + [editorActiveIndentGuide1]: '#292929', } }; /* -------------------------------- End hc-light theme -------------------------------- */ diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index 957920ebe67..acca9e7cde3 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -201,7 +201,7 @@ export function createCodeEditorServices(disposables: DisposableStore, services: define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService); define(ILanguageFeaturesService, LanguageFeaturesService); - const instantiationService = new TestInstantiationService(services, true); + const instantiationService = disposables.add(new TestInstantiationService(services, true)); disposables.add(toDisposable(() => { for (const id of serviceIdentifiers) { const instanceOrDescriptor = services.get(id); diff --git a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts index 06de7665c02..530ae84b562 100644 --- a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts +++ b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts @@ -921,7 +921,7 @@ suite('SplitLinesCollection', () => { })), [ { inlineDecorations: [{ startOffset: 8, endOffset: 23 }] }, - { inlineDecorations: [{ startOffset: 4, endOffset: 42 }] }, + { inlineDecorations: [{ startOffset: 4, endOffset: 30 }] }, { inlineDecorations: [{ startOffset: 4, endOffset: 16 }] }, { inlineDecorations: undefined }, { inlineDecorations: undefined }, diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts new file mode 100644 index 00000000000..85c9043e060 --- /dev/null +++ b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert = require('assert'); +import { UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; + +suite('DiffEditorWidget2', () => { + suite('UnchangedRegion', () => { + function serialize(regions: UnchangedRegion[]): unknown { + return regions.map(r => `${r.originalRange} - ${r.modifiedRange}`); + } + + test('Everything changed', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [new LineRangeMapping(new LineRange(1, 10), new LineRange(1, 10), [])], + 10, + 10, + )), []); + }); + + test('Nothing changed', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [], + 10, + 10, + )), [ + "[1,11) - [1,11)" + ]); + }); + + test('Change in the middle', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [new LineRangeMapping(new LineRange(50, 60), new LineRange(50, 60), [])], + 100, + 100, + )), ([ + '[1,47) - [1,47)', + '[63,101) - [63,101)' + ])); + }); + + test('Change at the end', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [new LineRangeMapping(new LineRange(99, 100), new LineRange(100, 100), [])], + 100, + 100, + )), (["[1,96) - [1,96)"])); + }); + }); +}); diff --git a/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts b/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts index 0e96cff2069..faaf6b520ec 100644 --- a/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts +++ b/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts @@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri'; suite('TextResourceConfigurationService - Update', () => { + let instantiationService: TestInstantiationService; let configurationValue: IConfigurationValue = {}; let updateArgs: any[]; const configurationService = new class extends TestConfigurationService { @@ -30,13 +31,17 @@ suite('TextResourceConfigurationService - Update', () => { let testObject: TextResourceConfigurationService; setup(() => { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); instantiationService.stub(IModelService, >{ getModel() { return null; } }); instantiationService.stub(ILanguageService, >{ guessLanguageIdByFilepathOrFirstLine() { return language; } }); instantiationService.stub(IConfigurationService, configurationService); testObject = instantiationService.createInstance(TextResourceConfigurationService); }); + teardown(() => { + instantiationService.dispose(); + }); + test('updateValue writes without target and overrides when no language is defined', async () => { const resource = URI.file('someFile'); await testObject.updateValue(resource, 'a', 'b'); diff --git a/src/vs/editor/test/node/diffing/diffingFixture.test.ts b/src/vs/editor/test/node/diffing/diffingFixture.test.ts index a66961863a3..59173290fc8 100644 --- a/src/vs/editor/test/node/diffing/diffingFixture.test.ts +++ b/src/vs/editor/test/node/diffing/diffingFixture.test.ts @@ -40,7 +40,8 @@ suite('diff fixtures', () => { const diffingAlgo = diffingAlgoName === 'legacy' ? new SmartLinesDiffComputer() : new StandardLinesDiffComputer(); - const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }); + const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0; + const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }); function getDiffs(changes: readonly LineRangeMapping[]): IDetailedDiff[] { return changes.map(c => ({ @@ -58,8 +59,8 @@ suite('diff fixtures', () => { modified: { content: secondContent, fileName: `./${secondFileName}` }, diffs: getDiffs(diff.changes), moves: diff.moves.map(v => ({ - originalRange: v.lineRangeMapping.originalRange.toString(), - modifiedRange: v.lineRangeMapping.modifiedRange.toString(), + originalRange: v.lineRangeMapping.original.toString(), + modifiedRange: v.lineRangeMapping.modified.toString(), changes: getDiffs(v.changes), })) }; @@ -112,7 +113,7 @@ suite('diff fixtures', () => { } test(`test`, () => { - runTest('issue-185779', 'advanced'); + runTest('invalid-diff-trimws', 'advanced'); }); for (const folder of folders) { diff --git a/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json index c6bc1333002..41e9321310a 100644 --- a/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json @@ -13,12 +13,8 @@ "modifiedRange": "[29,31)", "innerChanges": [ { - "originalRange": "[29,1 -> 33,1]", - "modifiedRange": "[29,1 -> 29,1]" - }, - { - "originalRange": "[33,14 -> 33,41]", - "modifiedRange": "[29,14 -> 30,54]" + "originalRange": "[29,1 -> 33,41]", + "modifiedRange": "[29,1 -> 30,54]" } ] }, @@ -47,16 +43,8 @@ "modifiedRange": "[36,37)", "innerChanges": [ { - "originalRange": "[41,9 -> 41,18]", - "modifiedRange": "[36,9 -> 36,44]" - }, - { - "originalRange": "[41,26 -> 42,34]", - "modifiedRange": "[36,52 -> 36,64]" - }, - { - "originalRange": "[43,1 -> 46,1]", - "modifiedRange": "[37,1 -> 37,1]" + "originalRange": "[41,9 -> 46,1]", + "modifiedRange": "[36,9 -> 37,1]" } ] }, @@ -65,12 +53,8 @@ "modifiedRange": "[39,40)", "innerChanges": [ { - "originalRange": "[48,9 -> 63,48]", - "modifiedRange": "[39,9 -> 39,43]" - }, - { - "originalRange": "[64,1 -> 72,1]", - "modifiedRange": "[40,1 -> 40,1]" + "originalRange": "[48,9 -> 72,1]", + "modifiedRange": "[39,9 -> 40,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/1.tst b/src/vs/editor/test/node/diffing/fixtures/deletion/1.tst new file mode 100644 index 00000000000..67dec2f85b3 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/deletion/1.tst @@ -0,0 +1,29 @@ +import { Link, List, Separator, Stack } from '@fluentui/react'; +import { View } from '../../layout/layout'; + +export const OtherToolsView = () => { + return ( + + + + { + if (!item?.name) { + return + } + return
{item!.name}
+ }} + > +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/2.tst b/src/vs/editor/test/node/diffing/fixtures/deletion/2.tst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json new file mode 100644 index 00000000000..77b3553a99c --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json @@ -0,0 +1,22 @@ +{ + "original": { + "content": "import { Link, List, Separator, Stack } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const OtherToolsView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\tif (!item?.name) {\n\t\t\t\t\t\t\t\treturn \n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn
{item!.name}
\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}", + "fileName": "./1.tst" + }, + "modified": { + "content": "", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[1,30)", + "modifiedRange": "[1,2)", + "innerChanges": [ + { + "originalRange": "[1,1 -> 29,64]", + "modifiedRange": "[1,1 -> 1,1]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json new file mode 100644 index 00000000000..c114d7e583e --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json @@ -0,0 +1,17 @@ +{ + "original": { + "content": "import { Link, List, Separator, Stack } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const OtherToolsView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\tif (!item?.name) {\n\t\t\t\t\t\t\t\treturn \n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn
{item!.name}
\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}", + "fileName": "./1.tst" + }, + "modified": { + "content": "", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[1,30)", + "modifiedRange": "[1,2)", + "innerChanges": null + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/difficult-move/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/difficult-move/advanced.expected.diff.json index 6d0a4cf9977..b2155f7f625 100644 --- a/src/vs/editor/test/node/diffing/fixtures/difficult-move/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/difficult-move/advanced.expected.diff.json @@ -43,60 +43,8 @@ "modifiedRange": "[226,234)", "innerChanges": [ { - "originalRange": "[222,17 -> 222,19]", - "modifiedRange": "[226,17 -> 226,17]" - }, - { - "originalRange": "[223,4 -> 223,28]", - "modifiedRange": "[227,4 -> 227,37]" - }, - { - "originalRange": "[223,32 -> 223,49]", - "modifiedRange": "[227,41 -> 227,48]" - }, - { - "originalRange": "[223,54 -> 223,65]", - "modifiedRange": "[227,53 -> 227,62]" - }, - { - "originalRange": "[224,4 -> 224,29]", - "modifiedRange": "[228,4 -> 228,55]" - }, - { - "originalRange": "[224,43 -> 225,63]", - "modifiedRange": "[228,69 -> 228,108]" - }, - { - "originalRange": "[225,78 -> 226,8]", - "modifiedRange": "[228,123 -> 228,152]" - }, - { - "originalRange": "[226,22 -> 226,25]", - "modifiedRange": "[228,166 -> 228,169]" - }, - { - "originalRange": "[227,5 -> 227,93]", - "modifiedRange": "[229,5 -> 229,67]" - }, - { - "originalRange": "[228,5 -> 228,51]", - "modifiedRange": "[230,5 -> 230,30]" - }, - { - "originalRange": "[229,6 -> 229,143]", - "modifiedRange": "[231,6 -> 231,40]" - }, - { - "originalRange": "[230,5 -> 232,42]", - "modifiedRange": "[232,5 -> 232,19]" - }, - { - "originalRange": "[232,48 -> 232,98]", - "modifiedRange": "[232,25 -> 233,58]" - }, - { - "originalRange": "[233,1 -> 234,1]", - "modifiedRange": "[234,1 -> 234,1]" + "originalRange": "[222,17 -> 234,1]", + "modifiedRange": "[226,17 -> 234,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/1.tst b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/1.tst new file mode 100644 index 00000000000..a859fd42b23 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/1.tst @@ -0,0 +1,815 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode'; +import TelemetryReporter from '@vscode/extension-telemetry'; +import { Repository, RepositoryState } from './repository'; +import { memoize, sequentialize, debounce } from './decorators'; +import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util'; +import { Git } from './git'; +import * as path from 'path'; +import * as fs from 'fs'; +import { fromGitUri } from './uri'; +import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git'; +import { Askpass } from './askpass'; +import { IPushErrorHandlerRegistry } from './pushError'; +import { ApiRepository } from './api/api1'; +import { IRemoteSourcePublisherRegistry } from './remotePublisher'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; +import { IBranchProtectionProviderRegistry } from './branchProtection'; + +class ClosedRepositoriesManager { + + private _repositories: Set; + get repositories(): string[] { + return [...this._repositories.values()]; + } + + constructor(private readonly workspaceState: Memento) { + this._repositories = new Set(workspaceState.get('closedRepositories', [])); + this.onDidChangeRepositories(); + } + + addRepository(repository: string): void { + this._repositories.add(repository); + this.onDidChangeRepositories(); + } + + deleteRepository(repository: string): boolean { + const result = this._repositories.delete(repository); + if (result) { + this.onDidChangeRepositories(); + } + + return result; + } + + isRepositoryClosed(repository: string): boolean { + return this._repositories.has(repository); + } + + private onDidChangeRepositories(): void { + this.workspaceState.update('closedRepositories', [...this._repositories.values()]); + commands.executeCommand('setContext', 'git.closedRepositoryCount', this._repositories.size); + } +} + +class ParentRepositoriesManager { + + /** + * Key - normalized path used in user interface + * Value - value indicating whether the repository should be opened + */ + private _repositories = new Set; + get repositories(): string[] { + return [...this._repositories.values()]; + } + + constructor(private readonly globalState: Memento) { + this.onDidChangeRepositories(); + } + + addRepository(repository: string): void { + this._repositories.add(repository); + this.onDidChangeRepositories(); + } + + deleteRepository(repository: string): boolean { + const result = this._repositories.delete(repository); + if (result) { + this.onDidChangeRepositories(); + } + + return result; + } + + hasRepository(repository: string): boolean { + return this._repositories.has(repository); + } + + openRepository(repository: string): void { + this.globalState.update(`parentRepository:${repository}`, true); + this.deleteRepository(repository); + } + + private onDidChangeRepositories(): void { + commands.executeCommand('setContext', 'git.parentRepositoryCount', this._repositories.size); + } +} + +class UnsafeRepositoriesManager { + + /** + * Key - normalized path used in user interface + * Value - path extracted from the output of the `git status` command + * used when calling `git config --global --add safe.directory` + */ + private _repositories = new Map(); + get repositories(): string[] { + return [...this._repositories.keys()]; + } + + constructor() { + this.onDidChangeRepositories(); + } + + addRepository(repository: string, path: string): void { + this._repositories.set(repository, path); + this.onDidChangeRepositories(); + } + + deleteRepository(repository: string): boolean { + const result = this._repositories.delete(repository); + if (result) { + this.onDidChangeRepositories(); + } + + return result; + } + + getRepositoryPath(repository: string): string | undefined { + return this._repositories.get(repository); + } + + hasRepository(repository: string): boolean { + return this._repositories.has(repository); + } + + private onDidChangeRepositories(): void { + commands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size); + } +} + +export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry { + + private _onDidOpenRepository = new EventEmitter(); + readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; + + private _onDidCloseRepository = new EventEmitter(); + readonly onDidCloseRepository: Event = this._onDidCloseRepository.event; + + private _onDidChangeRepository = new EventEmitter(); + readonly onDidChangeRepository: Event = this._onDidChangeRepository.event; + + private _onDidChangeOriginalResource = new EventEmitter(); + readonly onDidChangeOriginalResource: Event = this._onDidChangeOriginalResource.event; + + private openRepositories: OpenRepository[] = []; + get repositories(): Repository[] { return this.openRepositories.map(r => r.repository); } + + private possibleGitRepositoryPaths = new Set(); + + private _onDidChangeState = new EventEmitter(); + readonly onDidChangeState = this._onDidChangeState.event; + + private _onDidPublish = new EventEmitter(); + readonly onDidPublish = this._onDidPublish.event; + + firePublishEvent(repository: Repository, branch?: string) { + this._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch }); + } + + private _state: State = 'uninitialized'; + get state(): State { return this._state; } + + setState(state: State): void { + this._state = state; + this._onDidChangeState.fire(state); + commands.executeCommand('setContext', 'git.state', state); + } + + @memoize + get isInitialized(): Promise { + if (this._state === 'initialized') { + return Promise.resolve(); + } + + return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise; + } + + private remoteSourcePublishers = new Set(); + + private _onDidAddRemoteSourcePublisher = new EventEmitter(); + readonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event; + + private _onDidRemoveRemoteSourcePublisher = new EventEmitter(); + readonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event; + + private postCommitCommandsProviders = new Set(); + + private _onDidChangePostCommitCommandsProviders = new EventEmitter(); + readonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event; + + private branchProtectionProviders = new Map>(); + + private _onDidChangeBranchProtectionProviders = new EventEmitter(); + readonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event; + + private pushErrorHandlers = new Set(); + + private _unsafeRepositoriesManager: UnsafeRepositoriesManager; + get unsafeRepositories(): string[] { + return this._unsafeRepositoriesManager.repositories; + } + + private _parentRepositoriesManager: ParentRepositoriesManager; + get parentRepositories(): string[] { + return this._parentRepositoriesManager.repositories; + } + + private _closedRepositoriesManager: ClosedRepositoriesManager; + get closedRepositories(): string[] { + return [...this._closedRepositoriesManager.repositories]; + } + + /** + * We maintain a map containing both the path and the canonical path of the + * workspace folders. We are doing this as `git.exe` expands the symbolic links + * while there are scenarios in which VS Code does not. + * + * Key - path of the workspace folder + * Value - canonical path of the workspace folder + */ + private _workspaceFolders = new Map(); + + private disposables: Disposable[] = []; + + constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, readonly workspaceState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) { + // Repositories managers + this._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState); + this._parentRepositoriesManager = new ParentRepositoriesManager(globalState); + this._unsafeRepositoriesManager = new UnsafeRepositoriesManager(); + + workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); + window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); + workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); + + const fsWatcher = workspace.createFileSystemWatcher('**'); + this.disposables.push(fsWatcher); + + const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete); + const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git/.test(uri.path)); + const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri)); + onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables); + + this.setState('uninitialized'); + this.doInitialScan().finally(() => this.setState('initialized')); + } + + private async doInitialScan(): Promise { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt'); + + // Initial repository scan function + const initialScanFn = () => Promise.all([ + this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }), + this.onDidChangeVisibleTextEditors(window.visibleTextEditors), + this.scanWorkspaceFolders() + ]); + + if (config.get('showProgress', true)) { + await window.withProgress({ location: ProgressLocation.SourceControl }, initialScanFn); + } else { + await initialScanFn(); + } + + if (this.parentRepositories.length !== 0 && + parentRepositoryConfig === 'prompt') { + // Parent repositories notification + this.showParentRepositoryNotification(); + } else if (this.unsafeRepositories.length !== 0) { + // Unsafe repositories notification + this.showUnsafeRepositoryNotification(); + } + + /* __GDPR__ + "git.repositoryInitialScan" : { + "owner": "lszomoru", + "autoRepositoryDetection": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Setting that controls the initial repository scan" }, + "repositoryCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of repositories opened during initial repository scan" } + } + */ + this.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length }); + } + + /** + * Scans each workspace folder, looking for git repositories. By + * default it scans one level deep but that can be changed using + * the git.repositoryScanMaxDepth setting. + */ + private async scanWorkspaceFolders(): Promise { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + this.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`); + + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { + return; + } + + await Promise.all((workspace.workspaceFolders || []).map(async folder => { + const root = folder.uri.fsPath; + this.logger.trace(`[swsf] Workspace folder: ${root}`); + + // Workspace folder children + const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); + const repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []); + + const subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders)); + + // Repository scan folders + const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; + this.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); + + for (const scanPath of scanPaths) { + if (scanPath === '.git') { + this.logger.trace('[swsf] \'.git\' not supported in \'git.scanRepositories\' setting.'); + continue; + } + + if (path.isAbsolute(scanPath)) { + const notSupportedMessage = l10n.t('Absolute paths not supported in "git.scanRepositories" setting.'); + this.logger.warn(notSupportedMessage); + console.warn(notSupportedMessage); + continue; + } + + subfolders.add(path.join(root, scanPath)); + } + + this.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); + await Promise.all([...subfolders].map(f => this.openRepository(f))); + })); + } + + private async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise { + const result: string[] = []; + const foldersToTravers = [{ path: workspaceFolder, depth: 0 }]; + + while (foldersToTravers.length > 0) { + const currentFolder = foldersToTravers.shift()!; + + if (currentFolder.depth < maxDepth || maxDepth === -1) { + const children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true }); + const childrenFolders = children + .filter(dirent => + dirent.isDirectory() && dirent.name !== '.git' && + !repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f))) + .map(dirent => path.join(currentFolder.path, dirent.name)); + + result.push(...childrenFolders); + foldersToTravers.push(...childrenFolders.map(folder => { + return { path: folder, depth: currentFolder.depth + 1 }; + })); + } + } + + return result; + } + + private onPossibleGitRepositoryChange(uri: Uri): void { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + + if (autoRepositoryDetection === false) { + return; + } + + this.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\.git.*$/, '')); + } + + private eventuallyScanPossibleGitRepository(path: string) { + this.possibleGitRepositoryPaths.add(path); + this.eventuallyScanPossibleGitRepositories(); + } + + @debounce(500) + private eventuallyScanPossibleGitRepositories(): void { + for (const path of this.possibleGitRepositoryPaths) { + this.openRepository(path); + } + + this.possibleGitRepositoryPaths.clear(); + } + + private async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise { + const possibleRepositoryFolders = added + .filter(folder => !this.getOpenRepository(folder.uri)); + + const activeRepositoriesList = window.visibleTextEditors + .map(editor => this.getRepository(editor.document.uri)) + .filter(repository => !!repository) as Repository[]; + + const activeRepositories = new Set(activeRepositoriesList); + const openRepositoriesToDispose = removed + .map(folder => this.getOpenRepository(folder.uri)) + .filter(r => !!r) + .filter(r => !activeRepositories.has(r!.repository)) + .filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; + + openRepositoriesToDispose.forEach(r => r.dispose()); + this.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath))); + } + + private onDidChangeConfiguration(): void { + const possibleRepositoryFolders = (workspace.workspaceFolders || []) + .filter(folder => workspace.getConfiguration('git', folder.uri).get('enabled') === true) + .filter(folder => !this.getOpenRepository(folder.uri)); + + const openRepositoriesToDispose = this.openRepositories + .map(repository => ({ repository, root: Uri.file(repository.repository.root) })) + .filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true) + .map(({ repository }) => repository); + + this.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath)); + openRepositoriesToDispose.forEach(r => r.dispose()); + } + + private async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise { + if (!workspace.isTrusted) { + this.logger.trace('[svte] Workspace is not trusted.'); + return; + } + + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + this.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`); + + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') { + return; + } + + await Promise.all(editors.map(async editor => { + const uri = editor.document.uri; + + if (uri.scheme !== 'file') { + return; + } + + const repository = this.getRepository(uri); + + if (repository) { + this.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); + return; + } + + this.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`); + await this.openRepository(path.dirname(uri.fsPath)); + })); + } + + @sequentialize + async openRepository(repoPath: string, openIfClosed = false): Promise { + this.logger.trace(`Opening repository: ${repoPath}`); + const existingRepository = await this.getRepositoryExact(repoPath); + if (existingRepository) { + this.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`); + return; + } + + const config = workspace.getConfiguration('git', Uri.file(repoPath)); + const enabled = config.get('enabled') === true; + + if (!enabled) { + this.logger.trace('Git is not enabled'); + return; + } + + if (!workspace.isTrusted) { + // Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty + try { + fs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK); + const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']); + if (result.stderr.trim() === '' && result.stdout.trim() === '') { + this.logger.trace(`Bare repository: ${repoPath}`); + return; + } + } catch { + // If this throw, we should be good to open the repo (e.g. HEAD doesn't exist) + } + } + + try { + const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath); + this.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`); + + const existingRepository = await this.getRepositoryExact(repositoryRoot); + if (existingRepository) { + this.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); + return; + } + + if (this.shouldRepositoryBeIgnored(repositoryRoot)) { + this.logger.trace(`Repository for path ${repositoryRoot} is ignored`); + return; + } + + // Handle git repositories that are in parent folders + const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt'); + if (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) { + const isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot); + if (isRepositoryOutsideWorkspace) { + this.logger.trace(`Repository in parent folder: ${repositoryRoot}`); + + if (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) { + // Show a notification if the parent repository is opened after the initial scan + if (this.state === 'initialized' && parentRepositoryConfig === 'prompt') { + this.showParentRepositoryNotification(); + } + + this._parentRepositoriesManager.addRepository(repositoryRoot); + } + + return; + } + } + + // Handle unsafe repositories + if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) { + this.logger.trace(`Unsafe repository: ${repositoryRoot}`); + + // Show a notification if the unsafe repository is opened after the initial scan + if (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) { + this.showUnsafeRepositoryNotification(); + } + + this._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]); + + return; + } + + // Handle repositories that were closed by the user + if (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) { + this.logger.trace(`Repository for path ${repositoryRoot} is closed`); + return; + } + + // Open repository + const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); + const repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter); + + this.open(repository); + this._closedRepositoriesManager.deleteRepository(repository.root); + + // Do not await this, we want SCM + // to know about the repo asap + repository.status(); + } catch (err) { + // noop + this.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`); + } + } + + async openParentRepository(repoPath: string): Promise { + await this.openRepository(repoPath); + this._parentRepositoriesManager.openRepository(repoPath); + } + + private async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> { + try { + const rawRoot = await this.git.getRepositoryRoot(repoPath); + + // This can happen whenever `path` has the wrong case sensitivity in case + // insensitive file systems https://github.com/microsoft/vscode/issues/33498 + return { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null }; + } catch (err) { + // Handle unsafe repository + const unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \'([^']+)\'[\s\S]*git config --global --add safe\.directory '?([^'\n]+)'?$/m.exec(err.stderr); + if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) { + return { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch }; + } + + throw err; + } + } + + private shouldRepositoryBeIgnored(repositoryRoot: string): boolean { + const config = workspace.getConfiguration('git'); + const ignoredRepos = config.get('ignoredRepositories') || []; + + for (const ignoredRepo of ignoredRepos) { + if (path.isAbsolute(ignoredRepo)) { + if (pathEquals(ignoredRepo, repositoryRoot)) { + return true; + } + } else { + for (const folder of workspace.workspaceFolders || []) { + if (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) { + return true; + } + } + } + } + + return false; + } + + private open(repository: Repository): void { + this.logger.info(`Open repository: ${repository.root}`); + + const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); + const disappearListener = onDidDisappearRepository(() => dispose()); + const changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri })); + const originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri })); + + const shouldDetectSubmodules = workspace + .getConfiguration('git', Uri.file(repository.root)) + .get('detectSubmodules') as boolean; + + const submodulesLimit = workspace + .getConfiguration('git', Uri.file(repository.root)) + .get('detectSubmodulesLimit') as number; + + const checkForSubmodules = () => { + if (!shouldDetectSubmodules) { + this.logger.trace('Automatic detection of git submodules is not enabled.'); + return; + } + + if (repository.submodules.length > submodulesLimit) { + window.showWarningMessage(l10n.t('The "{0}" repository has {1} submodules which won\'t be opened automatically. You can still open each one individually by opening a file within.', path.basename(repository.root), repository.submodules.length)); + statusListener.dispose(); + } + + repository.submodules + .slice(0, submodulesLimit) + .map(r => path.join(repository.root, r.path)) + .forEach(p => { + this.logger.trace(`Opening submodule: '${p}'`); + this.eventuallyScanPossibleGitRepository(p); + }); + }; + + const updateMergeChanges = () => { + // set mergeChanges context + const mergeChanges: Uri[] = []; + for (const { repository } of this.openRepositories.values()) { + for (const state of repository.mergeGroup.resourceStates) { + mergeChanges.push(state.resourceUri); + } + } + commands.executeCommand('setContext', 'git.mergeChanges', mergeChanges); + }; + + const statusListener = repository.onDidRunGitStatus(() => { + checkForSubmodules(); + updateMergeChanges(); + }); + checkForSubmodules(); + + const updateOperationInProgressContext = () => { + let operationInProgress = false; + for (const { repository } of this.openRepositories.values()) { + if (repository.operations.shouldDisableCommands()) { + operationInProgress = true; + } + } + + commands.executeCommand('setContext', 'operationInProgress', operationInProgress); + }; + + const operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event); + const operationListener = operationEvent(() => updateOperationInProgressContext()); + updateOperationInProgressContext(); + + const dispose = () => { + disappearListener.dispose(); + changeListener.dispose(); + originalResourceChangeListener.dispose(); + statusListener.dispose(); + operationListener.dispose(); + repository.dispose(); + + this.openRepositories = this.openRepositories.filter(e => e !== openRepository); + this._onDidCloseRepository.fire(repository); + }; + + const openRepository = { repository, dispose }; + this.openRepositories.push(openRepository); + updateMergeChanges(); + this._onDidOpenRepository.fire(repository); + } + + close(repository: Repository): void { + const openRepository = this.getOpenRepository(repository); + + if (!openRepository) { + return; + } + + this.logger.info(`Close repository: ${repository.root}`); + this._closedRepositoriesManager.addRepository(openRepository.repository.root); + + openRepository.dispose(); + } + + async pickRepository(): Promise { + if (this.openRepositories.length === 0) { + throw new Error(l10n.t('There are no available repositories')); + } + + const picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index)); + const active = window.activeTextEditor; + const repository = active && this.getRepository(active.document.fileName); + const index = picks.findIndex(pick => pick.repository === repository); + + // Move repository pick containing the active text editor to appear first + if (index > -1) { + picks.unshift(...picks.splice(index, 1)); + } + + const placeHolder = l10n.t('Choose a repository'); + const pick = await window.showQuickPick(picks, { placeHolder }); + + return pick && pick.repository; + } + + getRepository(sourceControl: SourceControl): Repository | undefined; + getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined; + getRepository(path: string): Repository | undefined; + getRepository(resource: Uri): Repository | undefined; + getRepository(hint: any): Repository | undefined { + const liveRepository = this.getOpenRepository(hint); + return liveRepository && liveRepository.repository; + } + + private async getRepositoryExact(repoPath: string): Promise { + const repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); + const openRepository = this.openRepositories.find(async r => { + const rootPathCanonical = await fs.promises.realpath(r.repository.root, { encoding: 'utf8' }); + return pathEquals(rootPathCanonical, repoPathCanonical); + }); + return openRepository?.repository; + } + + private getOpenRepository(repository: Repository): OpenRepository | undefined; + private getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined; + private getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined; + private getOpenRepository(path: string): OpenRepository | undefined; + private getOpenRepository(resource: Uri): OpenRepository | undefined; + private getOpenRepository(hint: any): OpenRepository | undefined { + if (!hint) { + return undefined; + } + + if (hint instanceof Repository) { + return this.openRepositories.filter(r => r.repository === hint)[0]; + } + + if (hint instanceof ApiRepository) { + return this.openRepositories.filter(r => r.repository === hint.repository)[0]; + } + + if (typeof hint === 'string') { + hint = Uri.file(hint); + } + + if (hint instanceof Uri) { + let resourcePath: string; + + if (hint.scheme === 'git') { + resourcePath = fromGitUri(hint).path; + } else { + resourcePath = hint.fsPath; + } + + outer: + for (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) { + if (!isDescendant(liveRepository.repository.root, resourcePath)) { + continue; + } + + for (const submodule of liveRepository.repository.submodules) { + const submoduleRoot = path.join(liveRepository.repository.root, submodule.path); + + if (isDescendant(submoduleRoot, resourcePath)) { + continue outer; + } + } + + return liveRepository; + } + + return undefined; + } + + for (const liveRepository of this.openRepositories) { + const repository = liveRepository.repository; + + if (hint === repository.sourceControl) { + return liveRepository; + } + + if (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup || hint === repository.untrackedGroup) { + return liveRepository; + } + } + + return undefined; + } + +} diff --git a/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/2.tst b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/2.tst new file mode 100644 index 00000000000..d8d33a59a63 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/2.tst @@ -0,0 +1,832 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode'; +import TelemetryReporter from '@vscode/extension-telemetry'; +import { Repository, RepositoryState } from './repository'; +import { memoize, sequentialize, debounce } from './decorators'; +import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util'; +import { Git } from './git'; +import * as path from 'path'; +import * as fs from 'fs'; +import { fromGitUri } from './uri'; +import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git'; +import { Askpass } from './askpass'; +import { IPushErrorHandlerRegistry } from './pushError'; +import { ApiRepository } from './api/api1'; +import { IRemoteSourcePublisherRegistry } from './remotePublisher'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; +import { IBranchProtectionProviderRegistry } from './branchProtection'; + +class ClosedRepositoriesManager { + + private _repositories: Set; + get repositories(): string[] { + return [...this._repositories.values()]; + } + + constructor(private readonly workspaceState: Memento) { + this._repositories = new Set(workspaceState.get('closedRepositories', [])); + this.onDidChangeRepositories(); + } + + addRepository(repository: string): void { + this._repositories.add(repository); + this.onDidChangeRepositories(); + } + + deleteRepository(repository: string): boolean { + const result = this._repositories.delete(repository); + if (result) { + this.onDidChangeRepositories(); + } + + return result; + } + + isRepositoryClosed(repository: string): boolean { + return this._repositories.has(repository); + } + + private onDidChangeRepositories(): void { + this.workspaceState.update('closedRepositories', [...this._repositories.values()]); + commands.executeCommand('setContext', 'git.closedRepositoryCount', this._repositories.size); + } +} + +class ParentRepositoriesManager { + + /** + * Key - normalized path used in user interface + * Value - value indicating whether the repository should be opened + */ + private _repositories = new Set; + get repositories(): string[] { + return [...this._repositories.values()]; + } + + constructor(private readonly globalState: Memento) { + this.onDidChangeRepositories(); + } + + addRepository(repository: string): void { + this._repositories.add(repository); + this.onDidChangeRepositories(); + } + + deleteRepository(repository: string): boolean { + const result = this._repositories.delete(repository); + if (result) { + this.onDidChangeRepositories(); + } + + return result; + } + + hasRepository(repository: string): boolean { + return this._repositories.has(repository); + } + + openRepository(repository: string): void { + this.globalState.update(`parentRepository:${repository}`, true); + this.deleteRepository(repository); + } + + private onDidChangeRepositories(): void { + commands.executeCommand('setContext', 'git.parentRepositoryCount', this._repositories.size); + } +} + +class UnsafeRepositoriesManager { + + /** + * Key - normalized path used in user interface + * Value - path extracted from the output of the `git status` command + * used when calling `git config --global --add safe.directory` + */ + private _repositories = new Map(); + get repositories(): string[] { + return [...this._repositories.keys()]; + } + + constructor() { + this.onDidChangeRepositories(); + } + + addRepository(repository: string, path: string): void { + this._repositories.set(repository, path); + this.onDidChangeRepositories(); + } + + deleteRepository(repository: string): boolean { + const result = this._repositories.delete(repository); + if (result) { + this.onDidChangeRepositories(); + } + + return result; + } + + getRepositoryPath(repository: string): string | undefined { + return this._repositories.get(repository); + } + + hasRepository(repository: string): boolean { + return this._repositories.has(repository); + } + + private onDidChangeRepositories(): void { + commands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size); + } +} + +export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry { + + private _onDidOpenRepository = new EventEmitter(); + readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; + + private _onDidCloseRepository = new EventEmitter(); + readonly onDidCloseRepository: Event = this._onDidCloseRepository.event; + + private _onDidChangeRepository = new EventEmitter(); + readonly onDidChangeRepository: Event = this._onDidChangeRepository.event; + + private _onDidChangeOriginalResource = new EventEmitter(); + readonly onDidChangeOriginalResource: Event = this._onDidChangeOriginalResource.event; + + private openRepositories: OpenRepository[] = []; + get repositories(): Repository[] { return this.openRepositories.map(r => r.repository); } + + private possibleGitRepositoryPaths = new Set(); + + private _onDidChangeState = new EventEmitter(); + readonly onDidChangeState = this._onDidChangeState.event; + + private _onDidPublish = new EventEmitter(); + readonly onDidPublish = this._onDidPublish.event; + + firePublishEvent(repository: Repository, branch?: string) { + this._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch }); + } + + private _state: State = 'uninitialized'; + get state(): State { return this._state; } + + setState(state: State): void { + this._state = state; + this._onDidChangeState.fire(state); + commands.executeCommand('setContext', 'git.state', state); + } + + @memoize + get isInitialized(): Promise { + if (this._state === 'initialized') { + return Promise.resolve(); + } + + return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise; + } + + private remoteSourcePublishers = new Set(); + + private _onDidAddRemoteSourcePublisher = new EventEmitter(); + readonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event; + + private _onDidRemoveRemoteSourcePublisher = new EventEmitter(); + readonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event; + + private postCommitCommandsProviders = new Set(); + + private _onDidChangePostCommitCommandsProviders = new EventEmitter(); + readonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event; + + private branchProtectionProviders = new Map>(); + + private _onDidChangeBranchProtectionProviders = new EventEmitter(); + readonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event; + + private pushErrorHandlers = new Set(); + + private _unsafeRepositoriesManager: UnsafeRepositoriesManager; + get unsafeRepositories(): string[] { + return this._unsafeRepositoriesManager.repositories; + } + + private _parentRepositoriesManager: ParentRepositoriesManager; + get parentRepositories(): string[] { + return this._parentRepositoriesManager.repositories; + } + + private _closedRepositoriesManager: ClosedRepositoriesManager; + get closedRepositories(): string[] { + return [...this._closedRepositoriesManager.repositories]; + } + + /** + * We maintain a map containing both the path and the canonical path of the + * workspace folders. We are doing this as `git.exe` expands the symbolic links + * while there are scenarios in which VS Code does not. + * + * Key - path of the workspace folder + * Value - canonical path of the workspace folder + */ + private _workspaceFolders = new Map(); + + private disposables: Disposable[] = []; + + constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, readonly workspaceState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) { + // Repositories managers + this._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState); + this._parentRepositoriesManager = new ParentRepositoriesManager(globalState); + this._unsafeRepositoriesManager = new UnsafeRepositoriesManager(); + + workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); + window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); + workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); + + const fsWatcher = workspace.createFileSystemWatcher('**'); + this.disposables.push(fsWatcher); + + const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete); + const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git/.test(uri.path)); + const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri)); + onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables); + + this.setState('uninitialized'); + this.doInitialScan().finally(() => this.setState('initialized')); + } + + private async doInitialScan(): Promise { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt'); + + // Initial repository scan function + const initialScanFn = () => Promise.all([ + this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }), + this.onDidChangeVisibleTextEditors(window.visibleTextEditors), + this.scanWorkspaceFolders() + ]); + + if (config.get('showProgress', true)) { + await window.withProgress({ location: ProgressLocation.SourceControl }, initialScanFn); + } else { + await initialScanFn(); + } + + if (this.parentRepositories.length !== 0 && + parentRepositoryConfig === 'prompt') { + // Parent repositories notification + this.showParentRepositoryNotification(); + } else if (this.unsafeRepositories.length !== 0) { + // Unsafe repositories notification + this.showUnsafeRepositoryNotification(); + } + + /* __GDPR__ + "git.repositoryInitialScan" : { + "owner": "lszomoru", + "autoRepositoryDetection": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Setting that controls the initial repository scan" }, + "repositoryCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of repositories opened during initial repository scan" } + } + */ + this.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length }); + } + + /** + * Scans each workspace folder, looking for git repositories. By + * default it scans one level deep but that can be changed using + * the git.repositoryScanMaxDepth setting. + */ + private async scanWorkspaceFolders(): Promise { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + this.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`); + + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { + return; + } + + await Promise.all((workspace.workspaceFolders || []).map(async folder => { + const root = folder.uri.fsPath; + this.logger.trace(`[swsf] Workspace folder: ${root}`); + + // Workspace folder children + const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); + const repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []); + + const subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders)); + + // Repository scan folders + const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; + this.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); + + for (const scanPath of scanPaths) { + if (scanPath === '.git') { + this.logger.trace('[swsf] \'.git\' not supported in \'git.scanRepositories\' setting.'); + continue; + } + + if (path.isAbsolute(scanPath)) { + const notSupportedMessage = l10n.t('Absolute paths not supported in "git.scanRepositories" setting.'); + this.logger.warn(notSupportedMessage); + console.warn(notSupportedMessage); + continue; + } + + subfolders.add(path.join(root, scanPath)); + } + + this.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); + await Promise.all([...subfolders].map(f => this.openRepository(f))); + })); + } + + private async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise { + const result: string[] = []; + const foldersToTravers = [{ path: workspaceFolder, depth: 0 }]; + + while (foldersToTravers.length > 0) { + const currentFolder = foldersToTravers.shift()!; + + if (currentFolder.depth < maxDepth || maxDepth === -1) { + const children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true }); + const childrenFolders = children + .filter(dirent => + dirent.isDirectory() && dirent.name !== '.git' && + !repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f))) + .map(dirent => path.join(currentFolder.path, dirent.name)); + + result.push(...childrenFolders); + foldersToTravers.push(...childrenFolders.map(folder => { + return { path: folder, depth: currentFolder.depth + 1 }; + })); + } + } + + return result; + } + + private onPossibleGitRepositoryChange(uri: Uri): void { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + + if (autoRepositoryDetection === false) { + return; + } + + this.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\.git.*$/, '')); + } + + private eventuallyScanPossibleGitRepository(path: string) { + this.possibleGitRepositoryPaths.add(path); + this.eventuallyScanPossibleGitRepositories(); + } + + @debounce(500) + private eventuallyScanPossibleGitRepositories(): void { + for (const path of this.possibleGitRepositoryPaths) { + this.openRepository(path); + } + + this.possibleGitRepositoryPaths.clear(); + } + + private async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise { + const possibleRepositoryFolders = added + .filter(folder => !this.getOpenRepository(folder.uri)); + + const activeRepositoriesList = window.visibleTextEditors + .map(editor => this.getRepository(editor.document.uri)) + .filter(repository => !!repository) as Repository[]; + + const activeRepositories = new Set(activeRepositoriesList); + const openRepositoriesToDispose = removed + .map(folder => this.getOpenRepository(folder.uri)) + .filter(r => !!r) + .filter(r => !activeRepositories.has(r!.repository)) + .filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; + + openRepositoriesToDispose.forEach(r => r.dispose()); + this.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath))); + } + + private onDidChangeConfiguration(): void { + const possibleRepositoryFolders = (workspace.workspaceFolders || []) + .filter(folder => workspace.getConfiguration('git', folder.uri).get('enabled') === true) + .filter(folder => !this.getOpenRepository(folder.uri)); + + const openRepositoriesToDispose = this.openRepositories + .map(repository => ({ repository, root: Uri.file(repository.repository.root) })) + .filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true) + .map(({ repository }) => repository); + + this.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath)); + openRepositoriesToDispose.forEach(r => r.dispose()); + } + + private async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise { + if (!workspace.isTrusted) { + this.logger.trace('[svte] Workspace is not trusted.'); + return; + } + + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); + this.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`); + + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') { + return; + } + + await Promise.all(editors.map(async editor => { + const uri = editor.document.uri; + + if (uri.scheme !== 'file') { + return; + } + + const repository = this.getRepository(uri); + + if (repository) { + this.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); + return; + } + + this.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`); + await this.openRepository(path.dirname(uri.fsPath)); + })); + } + + @sequentialize + async openRepository(repoPath: string, openIfClosed = false): Promise { + this.logger.trace(`Opening repository: ${repoPath}`); + const existingRepository = await this.getRepositoryExact(repoPath); + if (existingRepository) { + this.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`); + return; + } + + const config = workspace.getConfiguration('git', Uri.file(repoPath)); + const enabled = config.get('enabled') === true; + + if (!enabled) { + this.logger.trace('Git is not enabled'); + return; + } + + if (!workspace.isTrusted) { + // Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty + try { + fs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK); + const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']); + if (result.stderr.trim() === '' && result.stdout.trim() === '') { + this.logger.trace(`Bare repository: ${repoPath}`); + return; + } + } catch { + // If this throw, we should be good to open the repo (e.g. HEAD doesn't exist) + } + } + + try { + const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath); + this.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`); + + const existingRepository = await this.getRepositoryExact(repositoryRoot); + if (existingRepository) { + this.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); + return; + } + + if (this.shouldRepositoryBeIgnored(repositoryRoot)) { + this.logger.trace(`Repository for path ${repositoryRoot} is ignored`); + return; + } + + // Handle git repositories that are in parent folders + const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt'); + if (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) { + const isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot); + if (isRepositoryOutsideWorkspace) { + this.logger.trace(`Repository in parent folder: ${repositoryRoot}`); + + if (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) { + // Show a notification if the parent repository is opened after the initial scan + if (this.state === 'initialized' && parentRepositoryConfig === 'prompt') { + this.showParentRepositoryNotification(); + } + + this._parentRepositoriesManager.addRepository(repositoryRoot); + } + + return; + } + } + + // Handle unsafe repositories + if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) { + this.logger.trace(`Unsafe repository: ${repositoryRoot}`); + + // Show a notification if the unsafe repository is opened after the initial scan + if (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) { + this.showUnsafeRepositoryNotification(); + } + + this._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]); + + return; + } + + // Handle repositories that were closed by the user + if (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) { + this.logger.trace(`Repository for path ${repositoryRoot} is closed`); + return; + } + + // Open repository + const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); + const repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter); + + this.open(repository); + this._closedRepositoriesManager.deleteRepository(repository.root); + + // Do not await this, we want SCM + // to know about the repo asap + repository.status(); + } catch (err) { + // noop + this.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`); + } + } + + async openParentRepository(repoPath: string): Promise { + await this.openRepository(repoPath); + this._parentRepositoriesManager.openRepository(repoPath); + } + + private async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> { + try { + const rawRoot = await this.git.getRepositoryRoot(repoPath); + + // This can happen whenever `path` has the wrong case sensitivity in case + // insensitive file systems https://github.com/microsoft/vscode/issues/33498 + return { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null }; + } catch (err) { + // Handle unsafe repository + const unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \'([^']+)\'[\s\S]*git config --global --add safe\.directory '?([^'\n]+)'?$/m.exec(err.stderr); + if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) { + return { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch }; + } + + throw err; + } + } + + private shouldRepositoryBeIgnored(repositoryRoot: string): boolean { + const config = workspace.getConfiguration('git'); + const ignoredRepos = config.get('ignoredRepositories') || []; + + for (const ignoredRepo of ignoredRepos) { + if (path.isAbsolute(ignoredRepo)) { + if (pathEquals(ignoredRepo, repositoryRoot)) { + return true; + } + } else { + for (const folder of workspace.workspaceFolders || []) { + if (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) { + return true; + } + } + } + } + + return false; + } + + private open(repository: Repository): void { + this.logger.info(`Open repository: ${repository.root}`); + + const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); + const disappearListener = onDidDisappearRepository(() => dispose()); + const changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri })); + const originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri })); + + const shouldDetectSubmodules = workspace + .getConfiguration('git', Uri.file(repository.root)) + .get('detectSubmodules') as boolean; + + const submodulesLimit = workspace + .getConfiguration('git', Uri.file(repository.root)) + .get('detectSubmodulesLimit') as number; + + const checkForSubmodules = () => { + if (!shouldDetectSubmodules) { + this.logger.trace('Automatic detection of git submodules is not enabled.'); + return; + } + + if (repository.submodules.length > submodulesLimit) { + window.showWarningMessage(l10n.t('The "{0}" repository has {1} submodules which won\'t be opened automatically. You can still open each one individually by opening a file within.', path.basename(repository.root), repository.submodules.length)); + statusListener.dispose(); + } + + repository.submodules + .slice(0, submodulesLimit) + .map(r => path.join(repository.root, r.path)) + .forEach(p => { + this.logger.trace(`Opening submodule: '${p}'`); + this.eventuallyScanPossibleGitRepository(p); + }); + }; + + const updateMergeChanges = () => { + // set mergeChanges context + const mergeChanges: Uri[] = []; + for (const { repository } of this.openRepositories.values()) { + for (const state of repository.mergeGroup.resourceStates) { + mergeChanges.push(state.resourceUri); + } + } + commands.executeCommand('setContext', 'git.mergeChanges', mergeChanges); + }; + + const statusListener = repository.onDidRunGitStatus(() => { + checkForSubmodules(); + updateMergeChanges(); + }); + checkForSubmodules(); + + const updateOperationInProgressContext = () => { + let operationInProgress = false; + for (const { repository } of this.openRepositories.values()) { + if (repository.operations.shouldDisableCommands()) { + operationInProgress = true; + } + } + + commands.executeCommand('setContext', 'operationInProgress', operationInProgress); + }; + + const operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event); + const operationListener = operationEvent(() => updateOperationInProgressContext()); + updateOperationInProgressContext(); + + const dispose = () => { + disappearListener.dispose(); + changeListener.dispose(); + originalResourceChangeListener.dispose(); + statusListener.dispose(); + operationListener.dispose(); + repository.dispose(); + + this.openRepositories = this.openRepositories.filter(e => e !== openRepository); + this._onDidCloseRepository.fire(repository); + }; + + const openRepository = { repository, dispose }; + this.openRepositories.push(openRepository); + updateMergeChanges(); + this._onDidOpenRepository.fire(repository); + } + + close(repository: Repository): void { + const openRepository = this.getOpenRepository(repository); + + if (!openRepository) { + return; + } + + this.logger.info(`Close repository: ${repository.root}`); + this._closedRepositoriesManager.addRepository(openRepository.repository.root); + + openRepository.dispose(); + } + + async pickRepository(): Promise { + if (this.openRepositories.length === 0) { + throw new Error(l10n.t('There are no available repositories')); + } + + const picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index)); + const active = window.activeTextEditor; + const repository = active && this.getRepository(active.document.fileName); + const index = picks.findIndex(pick => pick.repository === repository); + + // Move repository pick containing the active text editor to appear first + if (index > -1) { + picks.unshift(...picks.splice(index, 1)); + } + + const placeHolder = l10n.t('Choose a repository'); + const pick = await window.showQuickPick(picks, { placeHolder }); + + return pick && pick.repository; + } + + getRepository(sourceControl: SourceControl): Repository | undefined; + getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined; + getRepository(path: string): Repository | undefined; + getRepository(resource: Uri): Repository | undefined; + getRepository(hint: any): Repository | undefined { + const liveRepository = this.getOpenRepository(hint); + return liveRepository && liveRepository.repository; + } + + private async getRepositoryExact(repoPath: string): Promise { + const repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); + + for (const openRepository of this.openRepositories) { + const rootPathCanonical = await fs.promises.realpath(openRepository.repository.root, { encoding: 'utf8' }); + if (pathEquals(rootPathCanonical, repoPathCanonical)) { + return openRepository.repository; + } + } + + return undefined; + } + + private getOpenRepository(repository: Repository): OpenRepository | undefined; + private getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined; + private getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined; + private getOpenRepository(path: string): OpenRepository | undefined; + private getOpenRepository(resource: Uri): OpenRepository | undefined; + private getOpenRepository(hint: any): OpenRepository | undefined { + if (!hint) { + return undefined; + } + + if (hint instanceof Repository) { + return this.openRepositories.filter(r => r.repository === hint)[0]; + } + + if (hint instanceof ApiRepository) { + return this.openRepositories.filter(r => r.repository === hint.repository)[0]; + } + + if (typeof hint === 'string') { + hint = Uri.file(hint); + } + + if (hint instanceof Uri) { + let resourcePath: string; + + if (hint.scheme === 'git') { + resourcePath = fromGitUri(hint).path; + } else { + resourcePath = hint.fsPath; + } + + outer: + for (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) { + if (!isDescendant(liveRepository.repository.root, resourcePath)) { + continue; + } + + for (const submodule of liveRepository.repository.submodules) { + const submoduleRoot = path.join(liveRepository.repository.root, submodule.path); + + if (isDescendant(submoduleRoot, resourcePath)) { + continue outer; + } + } + + return liveRepository; + } + + return undefined; + } + + for (const liveRepository of this.openRepositories) { + const repository = liveRepository.repository; + + if (hint === repository.sourceControl) { + return liveRepository; + } + + if (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup || hint === repository.untrackedGroup) { + return liveRepository; + } + } + + return undefined; + } + + getRepositoryForSubmodule(submoduleUri: Uri): Repository | undefined { + for (const repository of this.repositories) { + for (const submodule of repository.submodules) { + const submodulePath = path.join(repository.root, submodule.path); + + if (submodulePath === submoduleUri.fsPath) { + return repository; + } + } + } + + return undefined; + } +} diff --git a/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json new file mode 100644 index 00000000000..6bfa1f36e92 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json @@ -0,0 +1,60 @@ +{ + "original": { + "content": "/*---------------------------------------------------------------------------------------------\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 *--------------------------------------------------------------------------------------------*/\n\nimport { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode';\nimport TelemetryReporter from '@vscode/extension-telemetry';\nimport { Repository, RepositoryState } from './repository';\nimport { memoize, sequentialize, debounce } from './decorators';\nimport { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util';\nimport { Git } from './git';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fromGitUri } from './uri';\nimport { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git';\nimport { Askpass } from './askpass';\nimport { IPushErrorHandlerRegistry } from './pushError';\nimport { ApiRepository } from './api/api1';\nimport { IRemoteSourcePublisherRegistry } from './remotePublisher';\nimport { IPostCommitCommandsProviderRegistry } from './postCommitCommands';\nimport { IBranchProtectionProviderRegistry } from './branchProtection';\n\nclass ClosedRepositoriesManager {\n\n\tprivate _repositories: Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly workspaceState: Memento) {\n\t\tthis._repositories = new Set(workspaceState.get('closedRepositories', []));\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tisRepositoryClosed(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tthis.workspaceState.update('closedRepositories', [...this._repositories.values()]);\n\t\tcommands.executeCommand('setContext', 'git.closedRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass ParentRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - value indicating whether the repository should be opened\n\t */\n\tprivate _repositories = new Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly globalState: Memento) {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\topenRepository(repository: string): void {\n\t\tthis.globalState.update(`parentRepository:${repository}`, true);\n\t\tthis.deleteRepository(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.parentRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass UnsafeRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - path extracted from the output of the `git status` command\n\t * used when calling `git config --global --add safe.directory`\n\t */\n\tprivate _repositories = new Map();\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.keys()];\n\t}\n\n\tconstructor() {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string, path: string): void {\n\t\tthis._repositories.set(repository, path);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tgetRepositoryPath(repository: string): string | undefined {\n\t\treturn this._repositories.get(repository);\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size);\n\t}\n}\n\nexport class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {\n\n\tprivate _onDidOpenRepository = new EventEmitter();\n\treadonly onDidOpenRepository: Event = this._onDidOpenRepository.event;\n\n\tprivate _onDidCloseRepository = new EventEmitter();\n\treadonly onDidCloseRepository: Event = this._onDidCloseRepository.event;\n\n\tprivate _onDidChangeRepository = new EventEmitter();\n\treadonly onDidChangeRepository: Event = this._onDidChangeRepository.event;\n\n\tprivate _onDidChangeOriginalResource = new EventEmitter();\n\treadonly onDidChangeOriginalResource: Event = this._onDidChangeOriginalResource.event;\n\n\tprivate openRepositories: OpenRepository[] = [];\n\tget repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }\n\n\tprivate possibleGitRepositoryPaths = new Set();\n\n\tprivate _onDidChangeState = new EventEmitter();\n\treadonly onDidChangeState = this._onDidChangeState.event;\n\n\tprivate _onDidPublish = new EventEmitter();\n\treadonly onDidPublish = this._onDidPublish.event;\n\n\tfirePublishEvent(repository: Repository, branch?: string) {\n\t\tthis._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch });\n\t}\n\n\tprivate _state: State = 'uninitialized';\n\tget state(): State { return this._state; }\n\n\tsetState(state: State): void {\n\t\tthis._state = state;\n\t\tthis._onDidChangeState.fire(state);\n\t\tcommands.executeCommand('setContext', 'git.state', state);\n\t}\n\n\t@memoize\n\tget isInitialized(): Promise {\n\t\tif (this._state === 'initialized') {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\treturn eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise;\n\t}\n\n\tprivate remoteSourcePublishers = new Set();\n\n\tprivate _onDidAddRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event;\n\n\tprivate _onDidRemoveRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event;\n\n\tprivate postCommitCommandsProviders = new Set();\n\n\tprivate _onDidChangePostCommitCommandsProviders = new EventEmitter();\n\treadonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event;\n\n\tprivate branchProtectionProviders = new Map>();\n\n\tprivate _onDidChangeBranchProtectionProviders = new EventEmitter();\n\treadonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event;\n\n\tprivate pushErrorHandlers = new Set();\n\n\tprivate _unsafeRepositoriesManager: UnsafeRepositoriesManager;\n\tget unsafeRepositories(): string[] {\n\t\treturn this._unsafeRepositoriesManager.repositories;\n\t}\n\n\tprivate _parentRepositoriesManager: ParentRepositoriesManager;\n\tget parentRepositories(): string[] {\n\t\treturn this._parentRepositoriesManager.repositories;\n\t}\n\n\tprivate _closedRepositoriesManager: ClosedRepositoriesManager;\n\tget closedRepositories(): string[] {\n\t\treturn [...this._closedRepositoriesManager.repositories];\n\t}\n\n\t/**\n\t * We maintain a map containing both the path and the canonical path of the\n\t * workspace folders. We are doing this as `git.exe` expands the symbolic links\n\t * while there are scenarios in which VS Code does not.\n\t *\n\t * Key - path of the workspace folder\n\t * Value - canonical path of the workspace folder\n\t */\n\tprivate _workspaceFolders = new Map();\n\n\tprivate disposables: Disposable[] = [];\n\n\tconstructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, readonly workspaceState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) {\n\t\t// Repositories managers\n\t\tthis._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState);\n\t\tthis._parentRepositoriesManager = new ParentRepositoriesManager(globalState);\n\t\tthis._unsafeRepositoriesManager = new UnsafeRepositoriesManager();\n\n\t\tworkspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);\n\t\twindow.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);\n\t\tworkspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);\n\n\t\tconst fsWatcher = workspace.createFileSystemWatcher('**');\n\t\tthis.disposables.push(fsWatcher);\n\n\t\tconst onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);\n\t\tconst onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\\/\\.git/.test(uri.path));\n\t\tconst onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));\n\t\tonPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);\n\n\t\tthis.setState('uninitialized');\n\t\tthis.doInitialScan().finally(() => this.setState('initialized'));\n\t}\n\n\tprivate async doInitialScan(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\n\t\t// Initial repository scan function\n\t\tconst initialScanFn = () => Promise.all([\n\t\t\tthis.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),\n\t\t\tthis.onDidChangeVisibleTextEditors(window.visibleTextEditors),\n\t\t\tthis.scanWorkspaceFolders()\n\t\t]);\n\n\t\tif (config.get('showProgress', true)) {\n\t\t\tawait window.withProgress({ location: ProgressLocation.SourceControl }, initialScanFn);\n\t\t} else {\n\t\t\tawait initialScanFn();\n\t\t}\n\n\t\tif (this.parentRepositories.length !== 0 &&\n\t\t\tparentRepositoryConfig === 'prompt') {\n\t\t\t// Parent repositories notification\n\t\t\tthis.showParentRepositoryNotification();\n\t\t} else if (this.unsafeRepositories.length !== 0) {\n\t\t\t// Unsafe repositories notification\n\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t}\n\n\t\t/* __GDPR__\n\t\t\t\"git.repositoryInitialScan\" : {\n\t\t\t\t\"owner\": \"lszomoru\",\n\t\t\t\t\"autoRepositoryDetection\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"comment\": \"Setting that controls the initial repository scan\" },\n\t\t\t\t\"repositoryCount\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"isMeasurement\": true, \"comment\": \"Number of repositories opened during initial repository scan\" }\n\t\t\t}\n\t\t*/\n\t\tthis.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length });\n\t}\n\n\t/**\n\t * Scans each workspace folder, looking for git repositories. By\n\t * default it scans one level deep but that can be changed using\n\t * the git.repositoryScanMaxDepth setting.\n\t */\n\tprivate async scanWorkspaceFolders(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all((workspace.workspaceFolders || []).map(async folder => {\n\t\t\tconst root = folder.uri.fsPath;\n\t\t\tthis.logger.trace(`[swsf] Workspace folder: ${root}`);\n\n\t\t\t// Workspace folder children\n\t\t\tconst repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1);\n\t\t\tconst repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []);\n\n\t\t\tconst subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders));\n\n\t\t\t// Repository scan folders\n\t\t\tconst scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || [];\n\t\t\tthis.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`);\n\n\t\t\tfor (const scanPath of scanPaths) {\n\t\t\t\tif (scanPath === '.git') {\n\t\t\t\t\tthis.logger.trace('[swsf] \\'.git\\' not supported in \\'git.scanRepositories\\' setting.');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (path.isAbsolute(scanPath)) {\n\t\t\t\t\tconst notSupportedMessage = l10n.t('Absolute paths not supported in \"git.scanRepositories\" setting.');\n\t\t\t\t\tthis.logger.warn(notSupportedMessage);\n\t\t\t\t\tconsole.warn(notSupportedMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tsubfolders.add(path.join(root, scanPath));\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`);\n\t\t\tawait Promise.all([...subfolders].map(f => this.openRepository(f)));\n\t\t}));\n\t}\n\n\tprivate async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise {\n\t\tconst result: string[] = [];\n\t\tconst foldersToTravers = [{ path: workspaceFolder, depth: 0 }];\n\n\t\twhile (foldersToTravers.length > 0) {\n\t\t\tconst currentFolder = foldersToTravers.shift()!;\n\n\t\t\tif (currentFolder.depth < maxDepth || maxDepth === -1) {\n\t\t\t\tconst children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true });\n\t\t\t\tconst childrenFolders = children\n\t\t\t\t\t.filter(dirent =>\n\t\t\t\t\t\tdirent.isDirectory() && dirent.name !== '.git' &&\n\t\t\t\t\t\t!repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f)))\n\t\t\t\t\t.map(dirent => path.join(currentFolder.path, dirent.name));\n\n\t\t\t\tresult.push(...childrenFolders);\n\t\t\t\tfoldersToTravers.push(...childrenFolders.map(folder => {\n\t\t\t\t\treturn { path: folder, depth: currentFolder.depth + 1 };\n\t\t\t\t}));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate onPossibleGitRepositoryChange(uri: Uri): void {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\n\t\tif (autoRepositoryDetection === false) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\\.git.*$/, ''));\n\t}\n\n\tprivate eventuallyScanPossibleGitRepository(path: string) {\n\t\tthis.possibleGitRepositoryPaths.add(path);\n\t\tthis.eventuallyScanPossibleGitRepositories();\n\t}\n\n\t@debounce(500)\n\tprivate eventuallyScanPossibleGitRepositories(): void {\n\t\tfor (const path of this.possibleGitRepositoryPaths) {\n\t\t\tthis.openRepository(path);\n\t\t}\n\n\t\tthis.possibleGitRepositoryPaths.clear();\n\t}\n\n\tprivate async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise {\n\t\tconst possibleRepositoryFolders = added\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst activeRepositoriesList = window.visibleTextEditors\n\t\t\t.map(editor => this.getRepository(editor.document.uri))\n\t\t\t.filter(repository => !!repository) as Repository[];\n\n\t\tconst activeRepositories = new Set(activeRepositoriesList);\n\t\tconst openRepositoriesToDispose = removed\n\t\t\t.map(folder => this.getOpenRepository(folder.uri))\n\t\t\t.filter(r => !!r)\n\t\t\t.filter(r => !activeRepositories.has(r!.repository))\n\t\t\t.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];\n\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tawait Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));\n\t}\n\n\tprivate onDidChangeConfiguration(): void {\n\t\tconst possibleRepositoryFolders = (workspace.workspaceFolders || [])\n\t\t\t.filter(folder => workspace.getConfiguration('git', folder.uri).get('enabled') === true)\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst openRepositoriesToDispose = this.openRepositories\n\t\t\t.map(repository => ({ repository, root: Uri.file(repository.repository.root) }))\n\t\t\t.filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true)\n\t\t\t.map(({ repository }) => repository);\n\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tpossibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t}\n\n\tprivate async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise {\n\t\tif (!workspace.isTrusted) {\n\t\t\tthis.logger.trace('[svte] Workspace is not trusted.');\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all(editors.map(async editor => {\n\t\t\tconst uri = editor.document.uri;\n\n\t\t\tif (uri.scheme !== 'file') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst repository = this.getRepository(uri);\n\n\t\t\tif (repository) {\n\t\t\t\tthis.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`);\n\t\t\tawait this.openRepository(path.dirname(uri.fsPath));\n\t\t}));\n\t}\n\n\t@sequentialize\n\tasync openRepository(repoPath: string, openIfClosed = false): Promise {\n\t\tthis.logger.trace(`Opening repository: ${repoPath}`);\n\t\tconst existingRepository = await this.getRepositoryExact(repoPath);\n\t\tif (existingRepository) {\n\t\t\tthis.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git', Uri.file(repoPath));\n\t\tconst enabled = config.get('enabled') === true;\n\n\t\tif (!enabled) {\n\t\t\tthis.logger.trace('Git is not enabled');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!workspace.isTrusted) {\n\t\t\t// Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty\n\t\t\ttry {\n\t\t\t\tfs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK);\n\t\t\t\tconst result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']);\n\t\t\t\tif (result.stderr.trim() === '' && result.stdout.trim() === '') {\n\t\t\t\t\tthis.logger.trace(`Bare repository: ${repoPath}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throw, we should be good to open the repo (e.g. HEAD doesn't exist)\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath);\n\t\t\tthis.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`);\n\n\t\t\tconst existingRepository = await this.getRepositoryExact(repositoryRoot);\n\t\t\tif (existingRepository) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.shouldRepositoryBeIgnored(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is ignored`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle git repositories that are in parent folders\n\t\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\t\t\tif (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) {\n\t\t\t\tconst isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot);\n\t\t\t\tif (isRepositoryOutsideWorkspace) {\n\t\t\t\t\tthis.logger.trace(`Repository in parent folder: ${repositoryRoot}`);\n\n\t\t\t\t\tif (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\t\t// Show a notification if the parent repository is opened after the initial scan\n\t\t\t\t\t\tif (this.state === 'initialized' && parentRepositoryConfig === 'prompt') {\n\t\t\t\t\t\t\tthis.showParentRepositoryNotification();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis._parentRepositoriesManager.addRepository(repositoryRoot);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle unsafe repositories\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\tthis.logger.trace(`Unsafe repository: ${repositoryRoot}`);\n\n\t\t\t\t// Show a notification if the unsafe repository is opened after the initial scan\n\t\t\t\tif (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t\t\t}\n\n\t\t\t\tthis._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle repositories that were closed by the user\n\t\t\tif (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is closed`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Open repository\n\t\t\tconst dotGit = await this.git.getRepositoryDotGit(repositoryRoot);\n\t\t\tconst repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter);\n\n\t\t\tthis.open(repository);\n\t\t\tthis._closedRepositoriesManager.deleteRepository(repository.root);\n\n\t\t\t// Do not await this, we want SCM\n\t\t\t// to know about the repo asap\n\t\t\trepository.status();\n\t\t} catch (err) {\n\t\t\t// noop\n\t\t\tthis.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`);\n\t\t}\n\t}\n\n\tasync openParentRepository(repoPath: string): Promise {\n\t\tawait this.openRepository(repoPath);\n\t\tthis._parentRepositoriesManager.openRepository(repoPath);\n\t}\n\n\tprivate async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> {\n\t\ttry {\n\t\t\tconst rawRoot = await this.git.getRepositoryRoot(repoPath);\n\n\t\t\t// This can happen whenever `path` has the wrong case sensitivity in case\n\t\t\t// insensitive file systems https://github.com/microsoft/vscode/issues/33498\n\t\t\treturn { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null };\n\t\t} catch (err) {\n\t\t\t// Handle unsafe repository\n\t\t\tconst unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \\'([^']+)\\'[\\s\\S]*git config --global --add safe\\.directory '?([^'\\n]+)'?$/m.exec(err.stderr);\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\treturn { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch };\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tprivate shouldRepositoryBeIgnored(repositoryRoot: string): boolean {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst ignoredRepos = config.get('ignoredRepositories') || [];\n\n\t\tfor (const ignoredRepo of ignoredRepos) {\n\t\t\tif (path.isAbsolute(ignoredRepo)) {\n\t\t\t\tif (pathEquals(ignoredRepo, repositoryRoot)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (const folder of workspace.workspaceFolders || []) {\n\t\t\t\t\tif (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate open(repository: Repository): void {\n\t\tthis.logger.info(`Open repository: ${repository.root}`);\n\n\t\tconst onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);\n\t\tconst disappearListener = onDidDisappearRepository(() => dispose());\n\t\tconst changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));\n\t\tconst originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));\n\n\t\tconst shouldDetectSubmodules = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodules') as boolean;\n\n\t\tconst submodulesLimit = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodulesLimit') as number;\n\n\t\tconst checkForSubmodules = () => {\n\t\t\tif (!shouldDetectSubmodules) {\n\t\t\t\tthis.logger.trace('Automatic detection of git submodules is not enabled.');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (repository.submodules.length > submodulesLimit) {\n\t\t\t\twindow.showWarningMessage(l10n.t('The \"{0}\" repository has {1} submodules which won\\'t be opened automatically. You can still open each one individually by opening a file within.', path.basename(repository.root), repository.submodules.length));\n\t\t\t\tstatusListener.dispose();\n\t\t\t}\n\n\t\t\trepository.submodules\n\t\t\t\t.slice(0, submodulesLimit)\n\t\t\t\t.map(r => path.join(repository.root, r.path))\n\t\t\t\t.forEach(p => {\n\t\t\t\t\tthis.logger.trace(`Opening submodule: '${p}'`);\n\t\t\t\t\tthis.eventuallyScanPossibleGitRepository(p);\n\t\t\t\t});\n\t\t};\n\n\t\tconst updateMergeChanges = () => {\n\t\t\t// set mergeChanges context\n\t\t\tconst mergeChanges: Uri[] = [];\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tfor (const state of repository.mergeGroup.resourceStates) {\n\t\t\t\t\tmergeChanges.push(state.resourceUri);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcommands.executeCommand('setContext', 'git.mergeChanges', mergeChanges);\n\t\t};\n\n\t\tconst statusListener = repository.onDidRunGitStatus(() => {\n\t\t\tcheckForSubmodules();\n\t\t\tupdateMergeChanges();\n\t\t});\n\t\tcheckForSubmodules();\n\n\t\tconst updateOperationInProgressContext = () => {\n\t\t\tlet operationInProgress = false;\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tif (repository.operations.shouldDisableCommands()) {\n\t\t\t\t\toperationInProgress = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcommands.executeCommand('setContext', 'operationInProgress', operationInProgress);\n\t\t};\n\n\t\tconst operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event);\n\t\tconst operationListener = operationEvent(() => updateOperationInProgressContext());\n\t\tupdateOperationInProgressContext();\n\n\t\tconst dispose = () => {\n\t\t\tdisappearListener.dispose();\n\t\t\tchangeListener.dispose();\n\t\t\toriginalResourceChangeListener.dispose();\n\t\t\tstatusListener.dispose();\n\t\t\toperationListener.dispose();\n\t\t\trepository.dispose();\n\n\t\t\tthis.openRepositories = this.openRepositories.filter(e => e !== openRepository);\n\t\t\tthis._onDidCloseRepository.fire(repository);\n\t\t};\n\n\t\tconst openRepository = { repository, dispose };\n\t\tthis.openRepositories.push(openRepository);\n\t\tupdateMergeChanges();\n\t\tthis._onDidOpenRepository.fire(repository);\n\t}\n\n\tclose(repository: Repository): void {\n\t\tconst openRepository = this.getOpenRepository(repository);\n\n\t\tif (!openRepository) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.logger.info(`Close repository: ${repository.root}`);\n\t\tthis._closedRepositoriesManager.addRepository(openRepository.repository.root);\n\n\t\topenRepository.dispose();\n\t}\n\n\tasync pickRepository(): Promise {\n\t\tif (this.openRepositories.length === 0) {\n\t\t\tthrow new Error(l10n.t('There are no available repositories'));\n\t\t}\n\n\t\tconst picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index));\n\t\tconst active = window.activeTextEditor;\n\t\tconst repository = active && this.getRepository(active.document.fileName);\n\t\tconst index = picks.findIndex(pick => pick.repository === repository);\n\n\t\t// Move repository pick containing the active text editor to appear first\n\t\tif (index > -1) {\n\t\t\tpicks.unshift(...picks.splice(index, 1));\n\t\t}\n\n\t\tconst placeHolder = l10n.t('Choose a repository');\n\t\tconst pick = await window.showQuickPick(picks, { placeHolder });\n\n\t\treturn pick && pick.repository;\n\t}\n\n\tgetRepository(sourceControl: SourceControl): Repository | undefined;\n\tgetRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;\n\tgetRepository(path: string): Repository | undefined;\n\tgetRepository(resource: Uri): Repository | undefined;\n\tgetRepository(hint: any): Repository | undefined {\n\t\tconst liveRepository = this.getOpenRepository(hint);\n\t\treturn liveRepository && liveRepository.repository;\n\t}\n\n\tprivate async getRepositoryExact(repoPath: string): Promise {\n\t\tconst repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' });\n\t\tconst openRepository = this.openRepositories.find(async r => {\n\t\t\tconst rootPathCanonical = await fs.promises.realpath(r.repository.root, { encoding: 'utf8' });\n\t\t\treturn pathEquals(rootPathCanonical, repoPathCanonical);\n\t\t});\n\t\treturn openRepository?.repository;\n\t}\n\n\tprivate getOpenRepository(repository: Repository): OpenRepository | undefined;\n\tprivate getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined;\n\tprivate getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined;\n\tprivate getOpenRepository(path: string): OpenRepository | undefined;\n\tprivate getOpenRepository(resource: Uri): OpenRepository | undefined;\n\tprivate getOpenRepository(hint: any): OpenRepository | undefined {\n\t\tif (!hint) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (hint instanceof Repository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint)[0];\n\t\t}\n\n\t\tif (hint instanceof ApiRepository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint.repository)[0];\n\t\t}\n\n\t\tif (typeof hint === 'string') {\n\t\t\thint = Uri.file(hint);\n\t\t}\n\n\t\tif (hint instanceof Uri) {\n\t\t\tlet resourcePath: string;\n\n\t\t\tif (hint.scheme === 'git') {\n\t\t\t\tresourcePath = fromGitUri(hint).path;\n\t\t\t} else {\n\t\t\t\tresourcePath = hint.fsPath;\n\t\t\t}\n\n\t\t\touter:\n\t\t\tfor (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) {\n\t\t\t\tif (!isDescendant(liveRepository.repository.root, resourcePath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const submodule of liveRepository.repository.submodules) {\n\t\t\t\t\tconst submoduleRoot = path.join(liveRepository.repository.root, submodule.path);\n\n\t\t\t\t\tif (isDescendant(submoduleRoot, resourcePath)) {\n\t\t\t\t\t\tcontinue outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tfor (const liveRepository of this.openRepositories) {\n\t\t\tconst repository = liveRepository.repository;\n\n\t\t\tif (hint === repository.sourceControl) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\tif (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup || hint === repository.untrackedGroup) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n}\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "/*---------------------------------------------------------------------------------------------\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 *--------------------------------------------------------------------------------------------*/\n\nimport { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode';\nimport TelemetryReporter from '@vscode/extension-telemetry';\nimport { Repository, RepositoryState } from './repository';\nimport { memoize, sequentialize, debounce } from './decorators';\nimport { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util';\nimport { Git } from './git';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fromGitUri } from './uri';\nimport { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git';\nimport { Askpass } from './askpass';\nimport { IPushErrorHandlerRegistry } from './pushError';\nimport { ApiRepository } from './api/api1';\nimport { IRemoteSourcePublisherRegistry } from './remotePublisher';\nimport { IPostCommitCommandsProviderRegistry } from './postCommitCommands';\nimport { IBranchProtectionProviderRegistry } from './branchProtection';\n\nclass ClosedRepositoriesManager {\n\n\tprivate _repositories: Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly workspaceState: Memento) {\n\t\tthis._repositories = new Set(workspaceState.get('closedRepositories', []));\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tisRepositoryClosed(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tthis.workspaceState.update('closedRepositories', [...this._repositories.values()]);\n\t\tcommands.executeCommand('setContext', 'git.closedRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass ParentRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - value indicating whether the repository should be opened\n\t */\n\tprivate _repositories = new Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly globalState: Memento) {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\topenRepository(repository: string): void {\n\t\tthis.globalState.update(`parentRepository:${repository}`, true);\n\t\tthis.deleteRepository(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.parentRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass UnsafeRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - path extracted from the output of the `git status` command\n\t * used when calling `git config --global --add safe.directory`\n\t */\n\tprivate _repositories = new Map();\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.keys()];\n\t}\n\n\tconstructor() {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string, path: string): void {\n\t\tthis._repositories.set(repository, path);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tgetRepositoryPath(repository: string): string | undefined {\n\t\treturn this._repositories.get(repository);\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size);\n\t}\n}\n\nexport class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {\n\n\tprivate _onDidOpenRepository = new EventEmitter();\n\treadonly onDidOpenRepository: Event = this._onDidOpenRepository.event;\n\n\tprivate _onDidCloseRepository = new EventEmitter();\n\treadonly onDidCloseRepository: Event = this._onDidCloseRepository.event;\n\n\tprivate _onDidChangeRepository = new EventEmitter();\n\treadonly onDidChangeRepository: Event = this._onDidChangeRepository.event;\n\n\tprivate _onDidChangeOriginalResource = new EventEmitter();\n\treadonly onDidChangeOriginalResource: Event = this._onDidChangeOriginalResource.event;\n\n\tprivate openRepositories: OpenRepository[] = [];\n\tget repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }\n\n\tprivate possibleGitRepositoryPaths = new Set();\n\n\tprivate _onDidChangeState = new EventEmitter();\n\treadonly onDidChangeState = this._onDidChangeState.event;\n\n\tprivate _onDidPublish = new EventEmitter();\n\treadonly onDidPublish = this._onDidPublish.event;\n\n\tfirePublishEvent(repository: Repository, branch?: string) {\n\t\tthis._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch });\n\t}\n\n\tprivate _state: State = 'uninitialized';\n\tget state(): State { return this._state; }\n\n\tsetState(state: State): void {\n\t\tthis._state = state;\n\t\tthis._onDidChangeState.fire(state);\n\t\tcommands.executeCommand('setContext', 'git.state', state);\n\t}\n\n\t@memoize\n\tget isInitialized(): Promise {\n\t\tif (this._state === 'initialized') {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\treturn eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise;\n\t}\n\n\tprivate remoteSourcePublishers = new Set();\n\n\tprivate _onDidAddRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event;\n\n\tprivate _onDidRemoveRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event;\n\n\tprivate postCommitCommandsProviders = new Set();\n\n\tprivate _onDidChangePostCommitCommandsProviders = new EventEmitter();\n\treadonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event;\n\n\tprivate branchProtectionProviders = new Map>();\n\n\tprivate _onDidChangeBranchProtectionProviders = new EventEmitter();\n\treadonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event;\n\n\tprivate pushErrorHandlers = new Set();\n\n\tprivate _unsafeRepositoriesManager: UnsafeRepositoriesManager;\n\tget unsafeRepositories(): string[] {\n\t\treturn this._unsafeRepositoriesManager.repositories;\n\t}\n\n\tprivate _parentRepositoriesManager: ParentRepositoriesManager;\n\tget parentRepositories(): string[] {\n\t\treturn this._parentRepositoriesManager.repositories;\n\t}\n\n\tprivate _closedRepositoriesManager: ClosedRepositoriesManager;\n\tget closedRepositories(): string[] {\n\t\treturn [...this._closedRepositoriesManager.repositories];\n\t}\n\n\t/**\n\t * We maintain a map containing both the path and the canonical path of the\n\t * workspace folders. We are doing this as `git.exe` expands the symbolic links\n\t * while there are scenarios in which VS Code does not.\n\t *\n\t * Key - path of the workspace folder\n\t * Value - canonical path of the workspace folder\n\t */\n\tprivate _workspaceFolders = new Map();\n\n\tprivate disposables: Disposable[] = [];\n\n\tconstructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, readonly workspaceState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) {\n\t\t// Repositories managers\n\t\tthis._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState);\n\t\tthis._parentRepositoriesManager = new ParentRepositoriesManager(globalState);\n\t\tthis._unsafeRepositoriesManager = new UnsafeRepositoriesManager();\n\n\t\tworkspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);\n\t\twindow.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);\n\t\tworkspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);\n\n\t\tconst fsWatcher = workspace.createFileSystemWatcher('**');\n\t\tthis.disposables.push(fsWatcher);\n\n\t\tconst onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);\n\t\tconst onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\\/\\.git/.test(uri.path));\n\t\tconst onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));\n\t\tonPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);\n\n\t\tthis.setState('uninitialized');\n\t\tthis.doInitialScan().finally(() => this.setState('initialized'));\n\t}\n\n\tprivate async doInitialScan(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\n\t\t// Initial repository scan function\n\t\tconst initialScanFn = () => Promise.all([\n\t\t\tthis.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),\n\t\t\tthis.onDidChangeVisibleTextEditors(window.visibleTextEditors),\n\t\t\tthis.scanWorkspaceFolders()\n\t\t]);\n\n\t\tif (config.get('showProgress', true)) {\n\t\t\tawait window.withProgress({ location: ProgressLocation.SourceControl }, initialScanFn);\n\t\t} else {\n\t\t\tawait initialScanFn();\n\t\t}\n\n\t\tif (this.parentRepositories.length !== 0 &&\n\t\t\tparentRepositoryConfig === 'prompt') {\n\t\t\t// Parent repositories notification\n\t\t\tthis.showParentRepositoryNotification();\n\t\t} else if (this.unsafeRepositories.length !== 0) {\n\t\t\t// Unsafe repositories notification\n\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t}\n\n\t\t/* __GDPR__\n\t\t\t\"git.repositoryInitialScan\" : {\n\t\t\t\t\"owner\": \"lszomoru\",\n\t\t\t\t\"autoRepositoryDetection\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"comment\": \"Setting that controls the initial repository scan\" },\n\t\t\t\t\"repositoryCount\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"isMeasurement\": true, \"comment\": \"Number of repositories opened during initial repository scan\" }\n\t\t\t}\n\t\t*/\n\t\tthis.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length });\n\t}\n\n\t/**\n\t * Scans each workspace folder, looking for git repositories. By\n\t * default it scans one level deep but that can be changed using\n\t * the git.repositoryScanMaxDepth setting.\n\t */\n\tprivate async scanWorkspaceFolders(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all((workspace.workspaceFolders || []).map(async folder => {\n\t\t\tconst root = folder.uri.fsPath;\n\t\t\tthis.logger.trace(`[swsf] Workspace folder: ${root}`);\n\n\t\t\t// Workspace folder children\n\t\t\tconst repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1);\n\t\t\tconst repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []);\n\n\t\t\tconst subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders));\n\n\t\t\t// Repository scan folders\n\t\t\tconst scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || [];\n\t\t\tthis.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`);\n\n\t\t\tfor (const scanPath of scanPaths) {\n\t\t\t\tif (scanPath === '.git') {\n\t\t\t\t\tthis.logger.trace('[swsf] \\'.git\\' not supported in \\'git.scanRepositories\\' setting.');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (path.isAbsolute(scanPath)) {\n\t\t\t\t\tconst notSupportedMessage = l10n.t('Absolute paths not supported in \"git.scanRepositories\" setting.');\n\t\t\t\t\tthis.logger.warn(notSupportedMessage);\n\t\t\t\t\tconsole.warn(notSupportedMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tsubfolders.add(path.join(root, scanPath));\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`);\n\t\t\tawait Promise.all([...subfolders].map(f => this.openRepository(f)));\n\t\t}));\n\t}\n\n\tprivate async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise {\n\t\tconst result: string[] = [];\n\t\tconst foldersToTravers = [{ path: workspaceFolder, depth: 0 }];\n\n\t\twhile (foldersToTravers.length > 0) {\n\t\t\tconst currentFolder = foldersToTravers.shift()!;\n\n\t\t\tif (currentFolder.depth < maxDepth || maxDepth === -1) {\n\t\t\t\tconst children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true });\n\t\t\t\tconst childrenFolders = children\n\t\t\t\t\t.filter(dirent =>\n\t\t\t\t\t\tdirent.isDirectory() && dirent.name !== '.git' &&\n\t\t\t\t\t\t!repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f)))\n\t\t\t\t\t.map(dirent => path.join(currentFolder.path, dirent.name));\n\n\t\t\t\tresult.push(...childrenFolders);\n\t\t\t\tfoldersToTravers.push(...childrenFolders.map(folder => {\n\t\t\t\t\treturn { path: folder, depth: currentFolder.depth + 1 };\n\t\t\t\t}));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate onPossibleGitRepositoryChange(uri: Uri): void {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\n\t\tif (autoRepositoryDetection === false) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\\.git.*$/, ''));\n\t}\n\n\tprivate eventuallyScanPossibleGitRepository(path: string) {\n\t\tthis.possibleGitRepositoryPaths.add(path);\n\t\tthis.eventuallyScanPossibleGitRepositories();\n\t}\n\n\t@debounce(500)\n\tprivate eventuallyScanPossibleGitRepositories(): void {\n\t\tfor (const path of this.possibleGitRepositoryPaths) {\n\t\t\tthis.openRepository(path);\n\t\t}\n\n\t\tthis.possibleGitRepositoryPaths.clear();\n\t}\n\n\tprivate async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise {\n\t\tconst possibleRepositoryFolders = added\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst activeRepositoriesList = window.visibleTextEditors\n\t\t\t.map(editor => this.getRepository(editor.document.uri))\n\t\t\t.filter(repository => !!repository) as Repository[];\n\n\t\tconst activeRepositories = new Set(activeRepositoriesList);\n\t\tconst openRepositoriesToDispose = removed\n\t\t\t.map(folder => this.getOpenRepository(folder.uri))\n\t\t\t.filter(r => !!r)\n\t\t\t.filter(r => !activeRepositories.has(r!.repository))\n\t\t\t.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];\n\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tawait Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));\n\t}\n\n\tprivate onDidChangeConfiguration(): void {\n\t\tconst possibleRepositoryFolders = (workspace.workspaceFolders || [])\n\t\t\t.filter(folder => workspace.getConfiguration('git', folder.uri).get('enabled') === true)\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst openRepositoriesToDispose = this.openRepositories\n\t\t\t.map(repository => ({ repository, root: Uri.file(repository.repository.root) }))\n\t\t\t.filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true)\n\t\t\t.map(({ repository }) => repository);\n\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tpossibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t}\n\n\tprivate async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise {\n\t\tif (!workspace.isTrusted) {\n\t\t\tthis.logger.trace('[svte] Workspace is not trusted.');\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all(editors.map(async editor => {\n\t\t\tconst uri = editor.document.uri;\n\n\t\t\tif (uri.scheme !== 'file') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst repository = this.getRepository(uri);\n\n\t\t\tif (repository) {\n\t\t\t\tthis.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`);\n\t\t\tawait this.openRepository(path.dirname(uri.fsPath));\n\t\t}));\n\t}\n\n\t@sequentialize\n\tasync openRepository(repoPath: string, openIfClosed = false): Promise {\n\t\tthis.logger.trace(`Opening repository: ${repoPath}`);\n\t\tconst existingRepository = await this.getRepositoryExact(repoPath);\n\t\tif (existingRepository) {\n\t\t\tthis.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git', Uri.file(repoPath));\n\t\tconst enabled = config.get('enabled') === true;\n\n\t\tif (!enabled) {\n\t\t\tthis.logger.trace('Git is not enabled');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!workspace.isTrusted) {\n\t\t\t// Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty\n\t\t\ttry {\n\t\t\t\tfs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK);\n\t\t\t\tconst result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']);\n\t\t\t\tif (result.stderr.trim() === '' && result.stdout.trim() === '') {\n\t\t\t\t\tthis.logger.trace(`Bare repository: ${repoPath}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throw, we should be good to open the repo (e.g. HEAD doesn't exist)\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath);\n\t\t\tthis.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`);\n\n\t\t\tconst existingRepository = await this.getRepositoryExact(repositoryRoot);\n\t\t\tif (existingRepository) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.shouldRepositoryBeIgnored(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is ignored`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle git repositories that are in parent folders\n\t\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\t\t\tif (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) {\n\t\t\t\tconst isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot);\n\t\t\t\tif (isRepositoryOutsideWorkspace) {\n\t\t\t\t\tthis.logger.trace(`Repository in parent folder: ${repositoryRoot}`);\n\n\t\t\t\t\tif (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\t\t// Show a notification if the parent repository is opened after the initial scan\n\t\t\t\t\t\tif (this.state === 'initialized' && parentRepositoryConfig === 'prompt') {\n\t\t\t\t\t\t\tthis.showParentRepositoryNotification();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis._parentRepositoriesManager.addRepository(repositoryRoot);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle unsafe repositories\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\tthis.logger.trace(`Unsafe repository: ${repositoryRoot}`);\n\n\t\t\t\t// Show a notification if the unsafe repository is opened after the initial scan\n\t\t\t\tif (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t\t\t}\n\n\t\t\t\tthis._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle repositories that were closed by the user\n\t\t\tif (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is closed`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Open repository\n\t\t\tconst dotGit = await this.git.getRepositoryDotGit(repositoryRoot);\n\t\t\tconst repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter);\n\n\t\t\tthis.open(repository);\n\t\t\tthis._closedRepositoriesManager.deleteRepository(repository.root);\n\n\t\t\t// Do not await this, we want SCM\n\t\t\t// to know about the repo asap\n\t\t\trepository.status();\n\t\t} catch (err) {\n\t\t\t// noop\n\t\t\tthis.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`);\n\t\t}\n\t}\n\n\tasync openParentRepository(repoPath: string): Promise {\n\t\tawait this.openRepository(repoPath);\n\t\tthis._parentRepositoriesManager.openRepository(repoPath);\n\t}\n\n\tprivate async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> {\n\t\ttry {\n\t\t\tconst rawRoot = await this.git.getRepositoryRoot(repoPath);\n\n\t\t\t// This can happen whenever `path` has the wrong case sensitivity in case\n\t\t\t// insensitive file systems https://github.com/microsoft/vscode/issues/33498\n\t\t\treturn { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null };\n\t\t} catch (err) {\n\t\t\t// Handle unsafe repository\n\t\t\tconst unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \\'([^']+)\\'[\\s\\S]*git config --global --add safe\\.directory '?([^'\\n]+)'?$/m.exec(err.stderr);\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\treturn { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch };\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tprivate shouldRepositoryBeIgnored(repositoryRoot: string): boolean {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst ignoredRepos = config.get('ignoredRepositories') || [];\n\n\t\tfor (const ignoredRepo of ignoredRepos) {\n\t\t\tif (path.isAbsolute(ignoredRepo)) {\n\t\t\t\tif (pathEquals(ignoredRepo, repositoryRoot)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (const folder of workspace.workspaceFolders || []) {\n\t\t\t\t\tif (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate open(repository: Repository): void {\n\t\tthis.logger.info(`Open repository: ${repository.root}`);\n\n\t\tconst onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);\n\t\tconst disappearListener = onDidDisappearRepository(() => dispose());\n\t\tconst changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));\n\t\tconst originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));\n\n\t\tconst shouldDetectSubmodules = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodules') as boolean;\n\n\t\tconst submodulesLimit = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodulesLimit') as number;\n\n\t\tconst checkForSubmodules = () => {\n\t\t\tif (!shouldDetectSubmodules) {\n\t\t\t\tthis.logger.trace('Automatic detection of git submodules is not enabled.');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (repository.submodules.length > submodulesLimit) {\n\t\t\t\twindow.showWarningMessage(l10n.t('The \"{0}\" repository has {1} submodules which won\\'t be opened automatically. You can still open each one individually by opening a file within.', path.basename(repository.root), repository.submodules.length));\n\t\t\t\tstatusListener.dispose();\n\t\t\t}\n\n\t\t\trepository.submodules\n\t\t\t\t.slice(0, submodulesLimit)\n\t\t\t\t.map(r => path.join(repository.root, r.path))\n\t\t\t\t.forEach(p => {\n\t\t\t\t\tthis.logger.trace(`Opening submodule: '${p}'`);\n\t\t\t\t\tthis.eventuallyScanPossibleGitRepository(p);\n\t\t\t\t});\n\t\t};\n\n\t\tconst updateMergeChanges = () => {\n\t\t\t// set mergeChanges context\n\t\t\tconst mergeChanges: Uri[] = [];\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tfor (const state of repository.mergeGroup.resourceStates) {\n\t\t\t\t\tmergeChanges.push(state.resourceUri);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcommands.executeCommand('setContext', 'git.mergeChanges', mergeChanges);\n\t\t};\n\n\t\tconst statusListener = repository.onDidRunGitStatus(() => {\n\t\t\tcheckForSubmodules();\n\t\t\tupdateMergeChanges();\n\t\t});\n\t\tcheckForSubmodules();\n\n\t\tconst updateOperationInProgressContext = () => {\n\t\t\tlet operationInProgress = false;\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tif (repository.operations.shouldDisableCommands()) {\n\t\t\t\t\toperationInProgress = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcommands.executeCommand('setContext', 'operationInProgress', operationInProgress);\n\t\t};\n\n\t\tconst operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event);\n\t\tconst operationListener = operationEvent(() => updateOperationInProgressContext());\n\t\tupdateOperationInProgressContext();\n\n\t\tconst dispose = () => {\n\t\t\tdisappearListener.dispose();\n\t\t\tchangeListener.dispose();\n\t\t\toriginalResourceChangeListener.dispose();\n\t\t\tstatusListener.dispose();\n\t\t\toperationListener.dispose();\n\t\t\trepository.dispose();\n\n\t\t\tthis.openRepositories = this.openRepositories.filter(e => e !== openRepository);\n\t\t\tthis._onDidCloseRepository.fire(repository);\n\t\t};\n\n\t\tconst openRepository = { repository, dispose };\n\t\tthis.openRepositories.push(openRepository);\n\t\tupdateMergeChanges();\n\t\tthis._onDidOpenRepository.fire(repository);\n\t}\n\n\tclose(repository: Repository): void {\n\t\tconst openRepository = this.getOpenRepository(repository);\n\n\t\tif (!openRepository) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.logger.info(`Close repository: ${repository.root}`);\n\t\tthis._closedRepositoriesManager.addRepository(openRepository.repository.root);\n\n\t\topenRepository.dispose();\n\t}\n\n\tasync pickRepository(): Promise {\n\t\tif (this.openRepositories.length === 0) {\n\t\t\tthrow new Error(l10n.t('There are no available repositories'));\n\t\t}\n\n\t\tconst picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index));\n\t\tconst active = window.activeTextEditor;\n\t\tconst repository = active && this.getRepository(active.document.fileName);\n\t\tconst index = picks.findIndex(pick => pick.repository === repository);\n\n\t\t// Move repository pick containing the active text editor to appear first\n\t\tif (index > -1) {\n\t\t\tpicks.unshift(...picks.splice(index, 1));\n\t\t}\n\n\t\tconst placeHolder = l10n.t('Choose a repository');\n\t\tconst pick = await window.showQuickPick(picks, { placeHolder });\n\n\t\treturn pick && pick.repository;\n\t}\n\n\tgetRepository(sourceControl: SourceControl): Repository | undefined;\n\tgetRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;\n\tgetRepository(path: string): Repository | undefined;\n\tgetRepository(resource: Uri): Repository | undefined;\n\tgetRepository(hint: any): Repository | undefined {\n\t\tconst liveRepository = this.getOpenRepository(hint);\n\t\treturn liveRepository && liveRepository.repository;\n\t}\n\n\tprivate async getRepositoryExact(repoPath: string): Promise {\n\t\tconst repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' });\n\n\t\tfor (const openRepository of this.openRepositories) {\n\t\t\tconst rootPathCanonical = await fs.promises.realpath(openRepository.repository.root, { encoding: 'utf8' });\n\t\t\tif (pathEquals(rootPathCanonical, repoPathCanonical)) {\n\t\t\t\treturn openRepository.repository;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate getOpenRepository(repository: Repository): OpenRepository | undefined;\n\tprivate getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined;\n\tprivate getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined;\n\tprivate getOpenRepository(path: string): OpenRepository | undefined;\n\tprivate getOpenRepository(resource: Uri): OpenRepository | undefined;\n\tprivate getOpenRepository(hint: any): OpenRepository | undefined {\n\t\tif (!hint) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (hint instanceof Repository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint)[0];\n\t\t}\n\n\t\tif (hint instanceof ApiRepository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint.repository)[0];\n\t\t}\n\n\t\tif (typeof hint === 'string') {\n\t\t\thint = Uri.file(hint);\n\t\t}\n\n\t\tif (hint instanceof Uri) {\n\t\t\tlet resourcePath: string;\n\n\t\t\tif (hint.scheme === 'git') {\n\t\t\t\tresourcePath = fromGitUri(hint).path;\n\t\t\t} else {\n\t\t\t\tresourcePath = hint.fsPath;\n\t\t\t}\n\n\t\t\touter:\n\t\t\tfor (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) {\n\t\t\t\tif (!isDescendant(liveRepository.repository.root, resourcePath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const submodule of liveRepository.repository.submodules) {\n\t\t\t\t\tconst submoduleRoot = path.join(liveRepository.repository.root, submodule.path);\n\n\t\t\t\t\tif (isDescendant(submoduleRoot, resourcePath)) {\n\t\t\t\t\t\tcontinue outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tfor (const liveRepository of this.openRepositories) {\n\t\t\tconst repository = liveRepository.repository;\n\n\t\t\tif (hint === repository.sourceControl) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\tif (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup || hint === repository.untrackedGroup) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tgetRepositoryForSubmodule(submoduleUri: Uri): Repository | undefined {\n\t\tfor (const repository of this.repositories) {\n\t\t\tfor (const submodule of repository.submodules) {\n\t\t\t\tconst submodulePath = path.join(repository.root, submodule.path);\n\n\t\t\t\tif (submodulePath === submoduleUri.fsPath) {\n\t\t\t\t\treturn repository;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[742,747)", + "modifiedRange": "[742,751)", + "innerChanges": [ + { + "originalRange": "[742,3 -> 742,3]", + "modifiedRange": "[742,1 -> 743,8]" + }, + { + "originalRange": "[742,24 -> 742,25]", + "modifiedRange": "[743,29 -> 743,31]" + }, + { + "originalRange": "[742,47 -> 742,63]", + "modifiedRange": "[743,53 -> 743,54]" + }, + { + "originalRange": "[743,57 -> 743,58]", + "modifiedRange": "[744,57 -> 744,71]" + }, + { + "originalRange": "[744,4 -> 744,11]", + "modifiedRange": "[745,4 -> 745,8]" + }, + { + "originalRange": "[744,59 -> 745,6]", + "modifiedRange": "[745,56 -> 745,59]" + }, + { + "originalRange": "[746,24 -> 746,25]", + "modifiedRange": "[746,26 -> 746,26]" + }, + { + "originalRange": "[747,1 -> 747,1]", + "modifiedRange": "[747,4 -> 751,1]" + } + ] + }, + { + "originalRange": "[815,815)", + "modifiedRange": "[819,832)", + "innerChanges": [ + { + "originalRange": "[815,1 -> 815,1]", + "modifiedRange": "[819,2 -> 832,1]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/legacy.expected.diff.json new file mode 100644 index 00000000000..e48b9d56df1 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/legacy.expected.diff.json @@ -0,0 +1,55 @@ +{ + "original": { + "content": "/*---------------------------------------------------------------------------------------------\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 *--------------------------------------------------------------------------------------------*/\n\nimport { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode';\nimport TelemetryReporter from '@vscode/extension-telemetry';\nimport { Repository, RepositoryState } from './repository';\nimport { memoize, sequentialize, debounce } from './decorators';\nimport { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util';\nimport { Git } from './git';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fromGitUri } from './uri';\nimport { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git';\nimport { Askpass } from './askpass';\nimport { IPushErrorHandlerRegistry } from './pushError';\nimport { ApiRepository } from './api/api1';\nimport { IRemoteSourcePublisherRegistry } from './remotePublisher';\nimport { IPostCommitCommandsProviderRegistry } from './postCommitCommands';\nimport { IBranchProtectionProviderRegistry } from './branchProtection';\n\nclass ClosedRepositoriesManager {\n\n\tprivate _repositories: Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly workspaceState: Memento) {\n\t\tthis._repositories = new Set(workspaceState.get('closedRepositories', []));\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tisRepositoryClosed(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tthis.workspaceState.update('closedRepositories', [...this._repositories.values()]);\n\t\tcommands.executeCommand('setContext', 'git.closedRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass ParentRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - value indicating whether the repository should be opened\n\t */\n\tprivate _repositories = new Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly globalState: Memento) {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\topenRepository(repository: string): void {\n\t\tthis.globalState.update(`parentRepository:${repository}`, true);\n\t\tthis.deleteRepository(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.parentRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass UnsafeRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - path extracted from the output of the `git status` command\n\t * used when calling `git config --global --add safe.directory`\n\t */\n\tprivate _repositories = new Map();\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.keys()];\n\t}\n\n\tconstructor() {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string, path: string): void {\n\t\tthis._repositories.set(repository, path);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tgetRepositoryPath(repository: string): string | undefined {\n\t\treturn this._repositories.get(repository);\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size);\n\t}\n}\n\nexport class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {\n\n\tprivate _onDidOpenRepository = new EventEmitter();\n\treadonly onDidOpenRepository: Event = this._onDidOpenRepository.event;\n\n\tprivate _onDidCloseRepository = new EventEmitter();\n\treadonly onDidCloseRepository: Event = this._onDidCloseRepository.event;\n\n\tprivate _onDidChangeRepository = new EventEmitter();\n\treadonly onDidChangeRepository: Event = this._onDidChangeRepository.event;\n\n\tprivate _onDidChangeOriginalResource = new EventEmitter();\n\treadonly onDidChangeOriginalResource: Event = this._onDidChangeOriginalResource.event;\n\n\tprivate openRepositories: OpenRepository[] = [];\n\tget repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }\n\n\tprivate possibleGitRepositoryPaths = new Set();\n\n\tprivate _onDidChangeState = new EventEmitter();\n\treadonly onDidChangeState = this._onDidChangeState.event;\n\n\tprivate _onDidPublish = new EventEmitter();\n\treadonly onDidPublish = this._onDidPublish.event;\n\n\tfirePublishEvent(repository: Repository, branch?: string) {\n\t\tthis._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch });\n\t}\n\n\tprivate _state: State = 'uninitialized';\n\tget state(): State { return this._state; }\n\n\tsetState(state: State): void {\n\t\tthis._state = state;\n\t\tthis._onDidChangeState.fire(state);\n\t\tcommands.executeCommand('setContext', 'git.state', state);\n\t}\n\n\t@memoize\n\tget isInitialized(): Promise {\n\t\tif (this._state === 'initialized') {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\treturn eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise;\n\t}\n\n\tprivate remoteSourcePublishers = new Set();\n\n\tprivate _onDidAddRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event;\n\n\tprivate _onDidRemoveRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event;\n\n\tprivate postCommitCommandsProviders = new Set();\n\n\tprivate _onDidChangePostCommitCommandsProviders = new EventEmitter();\n\treadonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event;\n\n\tprivate branchProtectionProviders = new Map>();\n\n\tprivate _onDidChangeBranchProtectionProviders = new EventEmitter();\n\treadonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event;\n\n\tprivate pushErrorHandlers = new Set();\n\n\tprivate _unsafeRepositoriesManager: UnsafeRepositoriesManager;\n\tget unsafeRepositories(): string[] {\n\t\treturn this._unsafeRepositoriesManager.repositories;\n\t}\n\n\tprivate _parentRepositoriesManager: ParentRepositoriesManager;\n\tget parentRepositories(): string[] {\n\t\treturn this._parentRepositoriesManager.repositories;\n\t}\n\n\tprivate _closedRepositoriesManager: ClosedRepositoriesManager;\n\tget closedRepositories(): string[] {\n\t\treturn [...this._closedRepositoriesManager.repositories];\n\t}\n\n\t/**\n\t * We maintain a map containing both the path and the canonical path of the\n\t * workspace folders. We are doing this as `git.exe` expands the symbolic links\n\t * while there are scenarios in which VS Code does not.\n\t *\n\t * Key - path of the workspace folder\n\t * Value - canonical path of the workspace folder\n\t */\n\tprivate _workspaceFolders = new Map();\n\n\tprivate disposables: Disposable[] = [];\n\n\tconstructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, readonly workspaceState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) {\n\t\t// Repositories managers\n\t\tthis._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState);\n\t\tthis._parentRepositoriesManager = new ParentRepositoriesManager(globalState);\n\t\tthis._unsafeRepositoriesManager = new UnsafeRepositoriesManager();\n\n\t\tworkspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);\n\t\twindow.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);\n\t\tworkspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);\n\n\t\tconst fsWatcher = workspace.createFileSystemWatcher('**');\n\t\tthis.disposables.push(fsWatcher);\n\n\t\tconst onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);\n\t\tconst onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\\/\\.git/.test(uri.path));\n\t\tconst onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));\n\t\tonPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);\n\n\t\tthis.setState('uninitialized');\n\t\tthis.doInitialScan().finally(() => this.setState('initialized'));\n\t}\n\n\tprivate async doInitialScan(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\n\t\t// Initial repository scan function\n\t\tconst initialScanFn = () => Promise.all([\n\t\t\tthis.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),\n\t\t\tthis.onDidChangeVisibleTextEditors(window.visibleTextEditors),\n\t\t\tthis.scanWorkspaceFolders()\n\t\t]);\n\n\t\tif (config.get('showProgress', true)) {\n\t\t\tawait window.withProgress({ location: ProgressLocation.SourceControl }, initialScanFn);\n\t\t} else {\n\t\t\tawait initialScanFn();\n\t\t}\n\n\t\tif (this.parentRepositories.length !== 0 &&\n\t\t\tparentRepositoryConfig === 'prompt') {\n\t\t\t// Parent repositories notification\n\t\t\tthis.showParentRepositoryNotification();\n\t\t} else if (this.unsafeRepositories.length !== 0) {\n\t\t\t// Unsafe repositories notification\n\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t}\n\n\t\t/* __GDPR__\n\t\t\t\"git.repositoryInitialScan\" : {\n\t\t\t\t\"owner\": \"lszomoru\",\n\t\t\t\t\"autoRepositoryDetection\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"comment\": \"Setting that controls the initial repository scan\" },\n\t\t\t\t\"repositoryCount\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"isMeasurement\": true, \"comment\": \"Number of repositories opened during initial repository scan\" }\n\t\t\t}\n\t\t*/\n\t\tthis.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length });\n\t}\n\n\t/**\n\t * Scans each workspace folder, looking for git repositories. By\n\t * default it scans one level deep but that can be changed using\n\t * the git.repositoryScanMaxDepth setting.\n\t */\n\tprivate async scanWorkspaceFolders(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all((workspace.workspaceFolders || []).map(async folder => {\n\t\t\tconst root = folder.uri.fsPath;\n\t\t\tthis.logger.trace(`[swsf] Workspace folder: ${root}`);\n\n\t\t\t// Workspace folder children\n\t\t\tconst repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1);\n\t\t\tconst repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []);\n\n\t\t\tconst subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders));\n\n\t\t\t// Repository scan folders\n\t\t\tconst scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || [];\n\t\t\tthis.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`);\n\n\t\t\tfor (const scanPath of scanPaths) {\n\t\t\t\tif (scanPath === '.git') {\n\t\t\t\t\tthis.logger.trace('[swsf] \\'.git\\' not supported in \\'git.scanRepositories\\' setting.');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (path.isAbsolute(scanPath)) {\n\t\t\t\t\tconst notSupportedMessage = l10n.t('Absolute paths not supported in \"git.scanRepositories\" setting.');\n\t\t\t\t\tthis.logger.warn(notSupportedMessage);\n\t\t\t\t\tconsole.warn(notSupportedMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tsubfolders.add(path.join(root, scanPath));\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`);\n\t\t\tawait Promise.all([...subfolders].map(f => this.openRepository(f)));\n\t\t}));\n\t}\n\n\tprivate async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise {\n\t\tconst result: string[] = [];\n\t\tconst foldersToTravers = [{ path: workspaceFolder, depth: 0 }];\n\n\t\twhile (foldersToTravers.length > 0) {\n\t\t\tconst currentFolder = foldersToTravers.shift()!;\n\n\t\t\tif (currentFolder.depth < maxDepth || maxDepth === -1) {\n\t\t\t\tconst children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true });\n\t\t\t\tconst childrenFolders = children\n\t\t\t\t\t.filter(dirent =>\n\t\t\t\t\t\tdirent.isDirectory() && dirent.name !== '.git' &&\n\t\t\t\t\t\t!repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f)))\n\t\t\t\t\t.map(dirent => path.join(currentFolder.path, dirent.name));\n\n\t\t\t\tresult.push(...childrenFolders);\n\t\t\t\tfoldersToTravers.push(...childrenFolders.map(folder => {\n\t\t\t\t\treturn { path: folder, depth: currentFolder.depth + 1 };\n\t\t\t\t}));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate onPossibleGitRepositoryChange(uri: Uri): void {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\n\t\tif (autoRepositoryDetection === false) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\\.git.*$/, ''));\n\t}\n\n\tprivate eventuallyScanPossibleGitRepository(path: string) {\n\t\tthis.possibleGitRepositoryPaths.add(path);\n\t\tthis.eventuallyScanPossibleGitRepositories();\n\t}\n\n\t@debounce(500)\n\tprivate eventuallyScanPossibleGitRepositories(): void {\n\t\tfor (const path of this.possibleGitRepositoryPaths) {\n\t\t\tthis.openRepository(path);\n\t\t}\n\n\t\tthis.possibleGitRepositoryPaths.clear();\n\t}\n\n\tprivate async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise {\n\t\tconst possibleRepositoryFolders = added\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst activeRepositoriesList = window.visibleTextEditors\n\t\t\t.map(editor => this.getRepository(editor.document.uri))\n\t\t\t.filter(repository => !!repository) as Repository[];\n\n\t\tconst activeRepositories = new Set(activeRepositoriesList);\n\t\tconst openRepositoriesToDispose = removed\n\t\t\t.map(folder => this.getOpenRepository(folder.uri))\n\t\t\t.filter(r => !!r)\n\t\t\t.filter(r => !activeRepositories.has(r!.repository))\n\t\t\t.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];\n\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tawait Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));\n\t}\n\n\tprivate onDidChangeConfiguration(): void {\n\t\tconst possibleRepositoryFolders = (workspace.workspaceFolders || [])\n\t\t\t.filter(folder => workspace.getConfiguration('git', folder.uri).get('enabled') === true)\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst openRepositoriesToDispose = this.openRepositories\n\t\t\t.map(repository => ({ repository, root: Uri.file(repository.repository.root) }))\n\t\t\t.filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true)\n\t\t\t.map(({ repository }) => repository);\n\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tpossibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t}\n\n\tprivate async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise {\n\t\tif (!workspace.isTrusted) {\n\t\t\tthis.logger.trace('[svte] Workspace is not trusted.');\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all(editors.map(async editor => {\n\t\t\tconst uri = editor.document.uri;\n\n\t\t\tif (uri.scheme !== 'file') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst repository = this.getRepository(uri);\n\n\t\t\tif (repository) {\n\t\t\t\tthis.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`);\n\t\t\tawait this.openRepository(path.dirname(uri.fsPath));\n\t\t}));\n\t}\n\n\t@sequentialize\n\tasync openRepository(repoPath: string, openIfClosed = false): Promise {\n\t\tthis.logger.trace(`Opening repository: ${repoPath}`);\n\t\tconst existingRepository = await this.getRepositoryExact(repoPath);\n\t\tif (existingRepository) {\n\t\t\tthis.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git', Uri.file(repoPath));\n\t\tconst enabled = config.get('enabled') === true;\n\n\t\tif (!enabled) {\n\t\t\tthis.logger.trace('Git is not enabled');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!workspace.isTrusted) {\n\t\t\t// Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty\n\t\t\ttry {\n\t\t\t\tfs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK);\n\t\t\t\tconst result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']);\n\t\t\t\tif (result.stderr.trim() === '' && result.stdout.trim() === '') {\n\t\t\t\t\tthis.logger.trace(`Bare repository: ${repoPath}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throw, we should be good to open the repo (e.g. HEAD doesn't exist)\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath);\n\t\t\tthis.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`);\n\n\t\t\tconst existingRepository = await this.getRepositoryExact(repositoryRoot);\n\t\t\tif (existingRepository) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.shouldRepositoryBeIgnored(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is ignored`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle git repositories that are in parent folders\n\t\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\t\t\tif (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) {\n\t\t\t\tconst isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot);\n\t\t\t\tif (isRepositoryOutsideWorkspace) {\n\t\t\t\t\tthis.logger.trace(`Repository in parent folder: ${repositoryRoot}`);\n\n\t\t\t\t\tif (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\t\t// Show a notification if the parent repository is opened after the initial scan\n\t\t\t\t\t\tif (this.state === 'initialized' && parentRepositoryConfig === 'prompt') {\n\t\t\t\t\t\t\tthis.showParentRepositoryNotification();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis._parentRepositoriesManager.addRepository(repositoryRoot);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle unsafe repositories\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\tthis.logger.trace(`Unsafe repository: ${repositoryRoot}`);\n\n\t\t\t\t// Show a notification if the unsafe repository is opened after the initial scan\n\t\t\t\tif (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t\t\t}\n\n\t\t\t\tthis._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle repositories that were closed by the user\n\t\t\tif (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is closed`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Open repository\n\t\t\tconst dotGit = await this.git.getRepositoryDotGit(repositoryRoot);\n\t\t\tconst repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter);\n\n\t\t\tthis.open(repository);\n\t\t\tthis._closedRepositoriesManager.deleteRepository(repository.root);\n\n\t\t\t// Do not await this, we want SCM\n\t\t\t// to know about the repo asap\n\t\t\trepository.status();\n\t\t} catch (err) {\n\t\t\t// noop\n\t\t\tthis.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`);\n\t\t}\n\t}\n\n\tasync openParentRepository(repoPath: string): Promise {\n\t\tawait this.openRepository(repoPath);\n\t\tthis._parentRepositoriesManager.openRepository(repoPath);\n\t}\n\n\tprivate async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> {\n\t\ttry {\n\t\t\tconst rawRoot = await this.git.getRepositoryRoot(repoPath);\n\n\t\t\t// This can happen whenever `path` has the wrong case sensitivity in case\n\t\t\t// insensitive file systems https://github.com/microsoft/vscode/issues/33498\n\t\t\treturn { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null };\n\t\t} catch (err) {\n\t\t\t// Handle unsafe repository\n\t\t\tconst unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \\'([^']+)\\'[\\s\\S]*git config --global --add safe\\.directory '?([^'\\n]+)'?$/m.exec(err.stderr);\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\treturn { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch };\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tprivate shouldRepositoryBeIgnored(repositoryRoot: string): boolean {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst ignoredRepos = config.get('ignoredRepositories') || [];\n\n\t\tfor (const ignoredRepo of ignoredRepos) {\n\t\t\tif (path.isAbsolute(ignoredRepo)) {\n\t\t\t\tif (pathEquals(ignoredRepo, repositoryRoot)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (const folder of workspace.workspaceFolders || []) {\n\t\t\t\t\tif (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate open(repository: Repository): void {\n\t\tthis.logger.info(`Open repository: ${repository.root}`);\n\n\t\tconst onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);\n\t\tconst disappearListener = onDidDisappearRepository(() => dispose());\n\t\tconst changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));\n\t\tconst originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));\n\n\t\tconst shouldDetectSubmodules = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodules') as boolean;\n\n\t\tconst submodulesLimit = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodulesLimit') as number;\n\n\t\tconst checkForSubmodules = () => {\n\t\t\tif (!shouldDetectSubmodules) {\n\t\t\t\tthis.logger.trace('Automatic detection of git submodules is not enabled.');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (repository.submodules.length > submodulesLimit) {\n\t\t\t\twindow.showWarningMessage(l10n.t('The \"{0}\" repository has {1} submodules which won\\'t be opened automatically. You can still open each one individually by opening a file within.', path.basename(repository.root), repository.submodules.length));\n\t\t\t\tstatusListener.dispose();\n\t\t\t}\n\n\t\t\trepository.submodules\n\t\t\t\t.slice(0, submodulesLimit)\n\t\t\t\t.map(r => path.join(repository.root, r.path))\n\t\t\t\t.forEach(p => {\n\t\t\t\t\tthis.logger.trace(`Opening submodule: '${p}'`);\n\t\t\t\t\tthis.eventuallyScanPossibleGitRepository(p);\n\t\t\t\t});\n\t\t};\n\n\t\tconst updateMergeChanges = () => {\n\t\t\t// set mergeChanges context\n\t\t\tconst mergeChanges: Uri[] = [];\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tfor (const state of repository.mergeGroup.resourceStates) {\n\t\t\t\t\tmergeChanges.push(state.resourceUri);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcommands.executeCommand('setContext', 'git.mergeChanges', mergeChanges);\n\t\t};\n\n\t\tconst statusListener = repository.onDidRunGitStatus(() => {\n\t\t\tcheckForSubmodules();\n\t\t\tupdateMergeChanges();\n\t\t});\n\t\tcheckForSubmodules();\n\n\t\tconst updateOperationInProgressContext = () => {\n\t\t\tlet operationInProgress = false;\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tif (repository.operations.shouldDisableCommands()) {\n\t\t\t\t\toperationInProgress = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcommands.executeCommand('setContext', 'operationInProgress', operationInProgress);\n\t\t};\n\n\t\tconst operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event);\n\t\tconst operationListener = operationEvent(() => updateOperationInProgressContext());\n\t\tupdateOperationInProgressContext();\n\n\t\tconst dispose = () => {\n\t\t\tdisappearListener.dispose();\n\t\t\tchangeListener.dispose();\n\t\t\toriginalResourceChangeListener.dispose();\n\t\t\tstatusListener.dispose();\n\t\t\toperationListener.dispose();\n\t\t\trepository.dispose();\n\n\t\t\tthis.openRepositories = this.openRepositories.filter(e => e !== openRepository);\n\t\t\tthis._onDidCloseRepository.fire(repository);\n\t\t};\n\n\t\tconst openRepository = { repository, dispose };\n\t\tthis.openRepositories.push(openRepository);\n\t\tupdateMergeChanges();\n\t\tthis._onDidOpenRepository.fire(repository);\n\t}\n\n\tclose(repository: Repository): void {\n\t\tconst openRepository = this.getOpenRepository(repository);\n\n\t\tif (!openRepository) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.logger.info(`Close repository: ${repository.root}`);\n\t\tthis._closedRepositoriesManager.addRepository(openRepository.repository.root);\n\n\t\topenRepository.dispose();\n\t}\n\n\tasync pickRepository(): Promise {\n\t\tif (this.openRepositories.length === 0) {\n\t\t\tthrow new Error(l10n.t('There are no available repositories'));\n\t\t}\n\n\t\tconst picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index));\n\t\tconst active = window.activeTextEditor;\n\t\tconst repository = active && this.getRepository(active.document.fileName);\n\t\tconst index = picks.findIndex(pick => pick.repository === repository);\n\n\t\t// Move repository pick containing the active text editor to appear first\n\t\tif (index > -1) {\n\t\t\tpicks.unshift(...picks.splice(index, 1));\n\t\t}\n\n\t\tconst placeHolder = l10n.t('Choose a repository');\n\t\tconst pick = await window.showQuickPick(picks, { placeHolder });\n\n\t\treturn pick && pick.repository;\n\t}\n\n\tgetRepository(sourceControl: SourceControl): Repository | undefined;\n\tgetRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;\n\tgetRepository(path: string): Repository | undefined;\n\tgetRepository(resource: Uri): Repository | undefined;\n\tgetRepository(hint: any): Repository | undefined {\n\t\tconst liveRepository = this.getOpenRepository(hint);\n\t\treturn liveRepository && liveRepository.repository;\n\t}\n\n\tprivate async getRepositoryExact(repoPath: string): Promise {\n\t\tconst repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' });\n\t\tconst openRepository = this.openRepositories.find(async r => {\n\t\t\tconst rootPathCanonical = await fs.promises.realpath(r.repository.root, { encoding: 'utf8' });\n\t\t\treturn pathEquals(rootPathCanonical, repoPathCanonical);\n\t\t});\n\t\treturn openRepository?.repository;\n\t}\n\n\tprivate getOpenRepository(repository: Repository): OpenRepository | undefined;\n\tprivate getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined;\n\tprivate getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined;\n\tprivate getOpenRepository(path: string): OpenRepository | undefined;\n\tprivate getOpenRepository(resource: Uri): OpenRepository | undefined;\n\tprivate getOpenRepository(hint: any): OpenRepository | undefined {\n\t\tif (!hint) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (hint instanceof Repository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint)[0];\n\t\t}\n\n\t\tif (hint instanceof ApiRepository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint.repository)[0];\n\t\t}\n\n\t\tif (typeof hint === 'string') {\n\t\t\thint = Uri.file(hint);\n\t\t}\n\n\t\tif (hint instanceof Uri) {\n\t\t\tlet resourcePath: string;\n\n\t\t\tif (hint.scheme === 'git') {\n\t\t\t\tresourcePath = fromGitUri(hint).path;\n\t\t\t} else {\n\t\t\t\tresourcePath = hint.fsPath;\n\t\t\t}\n\n\t\t\touter:\n\t\t\tfor (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) {\n\t\t\t\tif (!isDescendant(liveRepository.repository.root, resourcePath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const submodule of liveRepository.repository.submodules) {\n\t\t\t\t\tconst submoduleRoot = path.join(liveRepository.repository.root, submodule.path);\n\n\t\t\t\t\tif (isDescendant(submoduleRoot, resourcePath)) {\n\t\t\t\t\t\tcontinue outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tfor (const liveRepository of this.openRepositories) {\n\t\t\tconst repository = liveRepository.repository;\n\n\t\t\tif (hint === repository.sourceControl) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\tif (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup || hint === repository.untrackedGroup) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n}\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "/*---------------------------------------------------------------------------------------------\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 *--------------------------------------------------------------------------------------------*/\n\nimport { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode';\nimport TelemetryReporter from '@vscode/extension-telemetry';\nimport { Repository, RepositoryState } from './repository';\nimport { memoize, sequentialize, debounce } from './decorators';\nimport { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util';\nimport { Git } from './git';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { fromGitUri } from './uri';\nimport { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git';\nimport { Askpass } from './askpass';\nimport { IPushErrorHandlerRegistry } from './pushError';\nimport { ApiRepository } from './api/api1';\nimport { IRemoteSourcePublisherRegistry } from './remotePublisher';\nimport { IPostCommitCommandsProviderRegistry } from './postCommitCommands';\nimport { IBranchProtectionProviderRegistry } from './branchProtection';\n\nclass ClosedRepositoriesManager {\n\n\tprivate _repositories: Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly workspaceState: Memento) {\n\t\tthis._repositories = new Set(workspaceState.get('closedRepositories', []));\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tisRepositoryClosed(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tthis.workspaceState.update('closedRepositories', [...this._repositories.values()]);\n\t\tcommands.executeCommand('setContext', 'git.closedRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass ParentRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - value indicating whether the repository should be opened\n\t */\n\tprivate _repositories = new Set;\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.values()];\n\t}\n\n\tconstructor(private readonly globalState: Memento) {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string): void {\n\t\tthis._repositories.add(repository);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\topenRepository(repository: string): void {\n\t\tthis.globalState.update(`parentRepository:${repository}`, true);\n\t\tthis.deleteRepository(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.parentRepositoryCount', this._repositories.size);\n\t}\n}\n\nclass UnsafeRepositoriesManager {\n\n\t/**\n\t * Key - normalized path used in user interface\n\t * Value - path extracted from the output of the `git status` command\n\t * used when calling `git config --global --add safe.directory`\n\t */\n\tprivate _repositories = new Map();\n\tget repositories(): string[] {\n\t\treturn [...this._repositories.keys()];\n\t}\n\n\tconstructor() {\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\taddRepository(repository: string, path: string): void {\n\t\tthis._repositories.set(repository, path);\n\t\tthis.onDidChangeRepositories();\n\t}\n\n\tdeleteRepository(repository: string): boolean {\n\t\tconst result = this._repositories.delete(repository);\n\t\tif (result) {\n\t\t\tthis.onDidChangeRepositories();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tgetRepositoryPath(repository: string): string | undefined {\n\t\treturn this._repositories.get(repository);\n\t}\n\n\thasRepository(repository: string): boolean {\n\t\treturn this._repositories.has(repository);\n\t}\n\n\tprivate onDidChangeRepositories(): void {\n\t\tcommands.executeCommand('setContext', 'git.unsafeRepositoryCount', this._repositories.size);\n\t}\n}\n\nexport class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {\n\n\tprivate _onDidOpenRepository = new EventEmitter();\n\treadonly onDidOpenRepository: Event = this._onDidOpenRepository.event;\n\n\tprivate _onDidCloseRepository = new EventEmitter();\n\treadonly onDidCloseRepository: Event = this._onDidCloseRepository.event;\n\n\tprivate _onDidChangeRepository = new EventEmitter();\n\treadonly onDidChangeRepository: Event = this._onDidChangeRepository.event;\n\n\tprivate _onDidChangeOriginalResource = new EventEmitter();\n\treadonly onDidChangeOriginalResource: Event = this._onDidChangeOriginalResource.event;\n\n\tprivate openRepositories: OpenRepository[] = [];\n\tget repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }\n\n\tprivate possibleGitRepositoryPaths = new Set();\n\n\tprivate _onDidChangeState = new EventEmitter();\n\treadonly onDidChangeState = this._onDidChangeState.event;\n\n\tprivate _onDidPublish = new EventEmitter();\n\treadonly onDidPublish = this._onDidPublish.event;\n\n\tfirePublishEvent(repository: Repository, branch?: string) {\n\t\tthis._onDidPublish.fire({ repository: new ApiRepository(repository), branch: branch });\n\t}\n\n\tprivate _state: State = 'uninitialized';\n\tget state(): State { return this._state; }\n\n\tsetState(state: State): void {\n\t\tthis._state = state;\n\t\tthis._onDidChangeState.fire(state);\n\t\tcommands.executeCommand('setContext', 'git.state', state);\n\t}\n\n\t@memoize\n\tget isInitialized(): Promise {\n\t\tif (this._state === 'initialized') {\n\t\t\treturn Promise.resolve();\n\t\t}\n\n\t\treturn eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise;\n\t}\n\n\tprivate remoteSourcePublishers = new Set();\n\n\tprivate _onDidAddRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event;\n\n\tprivate _onDidRemoveRemoteSourcePublisher = new EventEmitter();\n\treadonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event;\n\n\tprivate postCommitCommandsProviders = new Set();\n\n\tprivate _onDidChangePostCommitCommandsProviders = new EventEmitter();\n\treadonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event;\n\n\tprivate branchProtectionProviders = new Map>();\n\n\tprivate _onDidChangeBranchProtectionProviders = new EventEmitter();\n\treadonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event;\n\n\tprivate pushErrorHandlers = new Set();\n\n\tprivate _unsafeRepositoriesManager: UnsafeRepositoriesManager;\n\tget unsafeRepositories(): string[] {\n\t\treturn this._unsafeRepositoriesManager.repositories;\n\t}\n\n\tprivate _parentRepositoriesManager: ParentRepositoriesManager;\n\tget parentRepositories(): string[] {\n\t\treturn this._parentRepositoriesManager.repositories;\n\t}\n\n\tprivate _closedRepositoriesManager: ClosedRepositoriesManager;\n\tget closedRepositories(): string[] {\n\t\treturn [...this._closedRepositoriesManager.repositories];\n\t}\n\n\t/**\n\t * We maintain a map containing both the path and the canonical path of the\n\t * workspace folders. We are doing this as `git.exe` expands the symbolic links\n\t * while there are scenarios in which VS Code does not.\n\t *\n\t * Key - path of the workspace folder\n\t * Value - canonical path of the workspace folder\n\t */\n\tprivate _workspaceFolders = new Map();\n\n\tprivate disposables: Disposable[] = [];\n\n\tconstructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, readonly workspaceState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) {\n\t\t// Repositories managers\n\t\tthis._closedRepositoriesManager = new ClosedRepositoriesManager(workspaceState);\n\t\tthis._parentRepositoriesManager = new ParentRepositoriesManager(globalState);\n\t\tthis._unsafeRepositoriesManager = new UnsafeRepositoriesManager();\n\n\t\tworkspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);\n\t\twindow.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);\n\t\tworkspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);\n\n\t\tconst fsWatcher = workspace.createFileSystemWatcher('**');\n\t\tthis.disposables.push(fsWatcher);\n\n\t\tconst onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);\n\t\tconst onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\\/\\.git/.test(uri.path));\n\t\tconst onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));\n\t\tonPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);\n\n\t\tthis.setState('uninitialized');\n\t\tthis.doInitialScan().finally(() => this.setState('initialized'));\n\t}\n\n\tprivate async doInitialScan(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\n\t\t// Initial repository scan function\n\t\tconst initialScanFn = () => Promise.all([\n\t\t\tthis.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),\n\t\t\tthis.onDidChangeVisibleTextEditors(window.visibleTextEditors),\n\t\t\tthis.scanWorkspaceFolders()\n\t\t]);\n\n\t\tif (config.get('showProgress', true)) {\n\t\t\tawait window.withProgress({ location: ProgressLocation.SourceControl }, initialScanFn);\n\t\t} else {\n\t\t\tawait initialScanFn();\n\t\t}\n\n\t\tif (this.parentRepositories.length !== 0 &&\n\t\t\tparentRepositoryConfig === 'prompt') {\n\t\t\t// Parent repositories notification\n\t\t\tthis.showParentRepositoryNotification();\n\t\t} else if (this.unsafeRepositories.length !== 0) {\n\t\t\t// Unsafe repositories notification\n\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t}\n\n\t\t/* __GDPR__\n\t\t\t\"git.repositoryInitialScan\" : {\n\t\t\t\t\"owner\": \"lszomoru\",\n\t\t\t\t\"autoRepositoryDetection\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"comment\": \"Setting that controls the initial repository scan\" },\n\t\t\t\t\"repositoryCount\": { \"classification\": \"SystemMetaData\", \"purpose\": \"FeatureInsight\", \"isMeasurement\": true, \"comment\": \"Number of repositories opened during initial repository scan\" }\n\t\t\t}\n\t\t*/\n\t\tthis.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length });\n\t}\n\n\t/**\n\t * Scans each workspace folder, looking for git repositories. By\n\t * default it scans one level deep but that can be changed using\n\t * the git.repositoryScanMaxDepth setting.\n\t */\n\tprivate async scanWorkspaceFolders(): Promise {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all((workspace.workspaceFolders || []).map(async folder => {\n\t\t\tconst root = folder.uri.fsPath;\n\t\t\tthis.logger.trace(`[swsf] Workspace folder: ${root}`);\n\n\t\t\t// Workspace folder children\n\t\t\tconst repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1);\n\t\t\tconst repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []);\n\n\t\t\tconst subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders));\n\n\t\t\t// Repository scan folders\n\t\t\tconst scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || [];\n\t\t\tthis.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`);\n\n\t\t\tfor (const scanPath of scanPaths) {\n\t\t\t\tif (scanPath === '.git') {\n\t\t\t\t\tthis.logger.trace('[swsf] \\'.git\\' not supported in \\'git.scanRepositories\\' setting.');\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (path.isAbsolute(scanPath)) {\n\t\t\t\t\tconst notSupportedMessage = l10n.t('Absolute paths not supported in \"git.scanRepositories\" setting.');\n\t\t\t\t\tthis.logger.warn(notSupportedMessage);\n\t\t\t\t\tconsole.warn(notSupportedMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tsubfolders.add(path.join(root, scanPath));\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`);\n\t\t\tawait Promise.all([...subfolders].map(f => this.openRepository(f)));\n\t\t}));\n\t}\n\n\tprivate async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise {\n\t\tconst result: string[] = [];\n\t\tconst foldersToTravers = [{ path: workspaceFolder, depth: 0 }];\n\n\t\twhile (foldersToTravers.length > 0) {\n\t\t\tconst currentFolder = foldersToTravers.shift()!;\n\n\t\t\tif (currentFolder.depth < maxDepth || maxDepth === -1) {\n\t\t\t\tconst children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true });\n\t\t\t\tconst childrenFolders = children\n\t\t\t\t\t.filter(dirent =>\n\t\t\t\t\t\tdirent.isDirectory() && dirent.name !== '.git' &&\n\t\t\t\t\t\t!repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f)))\n\t\t\t\t\t.map(dirent => path.join(currentFolder.path, dirent.name));\n\n\t\t\t\tresult.push(...childrenFolders);\n\t\t\t\tfoldersToTravers.push(...childrenFolders.map(folder => {\n\t\t\t\t\treturn { path: folder, depth: currentFolder.depth + 1 };\n\t\t\t\t}));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate onPossibleGitRepositoryChange(uri: Uri): void {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\n\t\tif (autoRepositoryDetection === false) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\\.git.*$/, ''));\n\t}\n\n\tprivate eventuallyScanPossibleGitRepository(path: string) {\n\t\tthis.possibleGitRepositoryPaths.add(path);\n\t\tthis.eventuallyScanPossibleGitRepositories();\n\t}\n\n\t@debounce(500)\n\tprivate eventuallyScanPossibleGitRepositories(): void {\n\t\tfor (const path of this.possibleGitRepositoryPaths) {\n\t\t\tthis.openRepository(path);\n\t\t}\n\n\t\tthis.possibleGitRepositoryPaths.clear();\n\t}\n\n\tprivate async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise {\n\t\tconst possibleRepositoryFolders = added\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst activeRepositoriesList = window.visibleTextEditors\n\t\t\t.map(editor => this.getRepository(editor.document.uri))\n\t\t\t.filter(repository => !!repository) as Repository[];\n\n\t\tconst activeRepositories = new Set(activeRepositoriesList);\n\t\tconst openRepositoriesToDispose = removed\n\t\t\t.map(folder => this.getOpenRepository(folder.uri))\n\t\t\t.filter(r => !!r)\n\t\t\t.filter(r => !activeRepositories.has(r!.repository))\n\t\t\t.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];\n\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tawait Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));\n\t}\n\n\tprivate onDidChangeConfiguration(): void {\n\t\tconst possibleRepositoryFolders = (workspace.workspaceFolders || [])\n\t\t\t.filter(folder => workspace.getConfiguration('git', folder.uri).get('enabled') === true)\n\t\t\t.filter(folder => !this.getOpenRepository(folder.uri));\n\n\t\tconst openRepositoriesToDispose = this.openRepositories\n\t\t\t.map(repository => ({ repository, root: Uri.file(repository.repository.root) }))\n\t\t\t.filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true)\n\t\t\t.map(({ repository }) => repository);\n\n\t\tthis.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`);\n\t\tpossibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));\n\t\topenRepositoriesToDispose.forEach(r => r.dispose());\n\t}\n\n\tprivate async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise {\n\t\tif (!workspace.isTrusted) {\n\t\t\tthis.logger.trace('[svte] Workspace is not trusted.');\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst autoRepositoryDetection = config.get('autoRepositoryDetection');\n\t\tthis.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`);\n\n\t\tif (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') {\n\t\t\treturn;\n\t\t}\n\n\t\tawait Promise.all(editors.map(async editor => {\n\t\t\tconst uri = editor.document.uri;\n\n\t\t\tif (uri.scheme !== 'file') {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst repository = this.getRepository(uri);\n\n\t\t\tif (repository) {\n\t\t\t\tthis.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`);\n\t\t\tawait this.openRepository(path.dirname(uri.fsPath));\n\t\t}));\n\t}\n\n\t@sequentialize\n\tasync openRepository(repoPath: string, openIfClosed = false): Promise {\n\t\tthis.logger.trace(`Opening repository: ${repoPath}`);\n\t\tconst existingRepository = await this.getRepositoryExact(repoPath);\n\t\tif (existingRepository) {\n\t\t\tthis.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst config = workspace.getConfiguration('git', Uri.file(repoPath));\n\t\tconst enabled = config.get('enabled') === true;\n\n\t\tif (!enabled) {\n\t\t\tthis.logger.trace('Git is not enabled');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!workspace.isTrusted) {\n\t\t\t// Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty\n\t\t\ttry {\n\t\t\t\tfs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK);\n\t\t\t\tconst result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']);\n\t\t\t\tif (result.stderr.trim() === '' && result.stdout.trim() === '') {\n\t\t\t\t\tthis.logger.trace(`Bare repository: ${repoPath}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throw, we should be good to open the repo (e.g. HEAD doesn't exist)\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath);\n\t\t\tthis.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`);\n\n\t\t\tconst existingRepository = await this.getRepositoryExact(repositoryRoot);\n\t\t\tif (existingRepository) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.shouldRepositoryBeIgnored(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is ignored`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle git repositories that are in parent folders\n\t\t\tconst parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');\n\t\t\tif (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) {\n\t\t\t\tconst isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot);\n\t\t\t\tif (isRepositoryOutsideWorkspace) {\n\t\t\t\t\tthis.logger.trace(`Repository in parent folder: ${repositoryRoot}`);\n\n\t\t\t\t\tif (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\t\t// Show a notification if the parent repository is opened after the initial scan\n\t\t\t\t\t\tif (this.state === 'initialized' && parentRepositoryConfig === 'prompt') {\n\t\t\t\t\t\t\tthis.showParentRepositoryNotification();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis._parentRepositoriesManager.addRepository(repositoryRoot);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle unsafe repositories\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\tthis.logger.trace(`Unsafe repository: ${repositoryRoot}`);\n\n\t\t\t\t// Show a notification if the unsafe repository is opened after the initial scan\n\t\t\t\tif (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) {\n\t\t\t\t\tthis.showUnsafeRepositoryNotification();\n\t\t\t\t}\n\n\t\t\t\tthis._unsafeRepositoriesManager.addRepository(repositoryRoot, unsafeRepositoryMatch[2]);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handle repositories that were closed by the user\n\t\t\tif (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) {\n\t\t\t\tthis.logger.trace(`Repository for path ${repositoryRoot} is closed`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Open repository\n\t\t\tconst dotGit = await this.git.getRepositoryDotGit(repositoryRoot);\n\t\t\tconst repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter);\n\n\t\t\tthis.open(repository);\n\t\t\tthis._closedRepositoriesManager.deleteRepository(repository.root);\n\n\t\t\t// Do not await this, we want SCM\n\t\t\t// to know about the repo asap\n\t\t\trepository.status();\n\t\t} catch (err) {\n\t\t\t// noop\n\t\t\tthis.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`);\n\t\t}\n\t}\n\n\tasync openParentRepository(repoPath: string): Promise {\n\t\tawait this.openRepository(repoPath);\n\t\tthis._parentRepositoriesManager.openRepository(repoPath);\n\t}\n\n\tprivate async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> {\n\t\ttry {\n\t\t\tconst rawRoot = await this.git.getRepositoryRoot(repoPath);\n\n\t\t\t// This can happen whenever `path` has the wrong case sensitivity in case\n\t\t\t// insensitive file systems https://github.com/microsoft/vscode/issues/33498\n\t\t\treturn { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null };\n\t\t} catch (err) {\n\t\t\t// Handle unsafe repository\n\t\t\tconst unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \\'([^']+)\\'[\\s\\S]*git config --global --add safe\\.directory '?([^'\\n]+)'?$/m.exec(err.stderr);\n\t\t\tif (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {\n\t\t\t\treturn { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch };\n\t\t\t}\n\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tprivate shouldRepositoryBeIgnored(repositoryRoot: string): boolean {\n\t\tconst config = workspace.getConfiguration('git');\n\t\tconst ignoredRepos = config.get('ignoredRepositories') || [];\n\n\t\tfor (const ignoredRepo of ignoredRepos) {\n\t\t\tif (path.isAbsolute(ignoredRepo)) {\n\t\t\t\tif (pathEquals(ignoredRepo, repositoryRoot)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (const folder of workspace.workspaceFolders || []) {\n\t\t\t\t\tif (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate open(repository: Repository): void {\n\t\tthis.logger.info(`Open repository: ${repository.root}`);\n\n\t\tconst onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);\n\t\tconst disappearListener = onDidDisappearRepository(() => dispose());\n\t\tconst changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));\n\t\tconst originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));\n\n\t\tconst shouldDetectSubmodules = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodules') as boolean;\n\n\t\tconst submodulesLimit = workspace\n\t\t\t.getConfiguration('git', Uri.file(repository.root))\n\t\t\t.get('detectSubmodulesLimit') as number;\n\n\t\tconst checkForSubmodules = () => {\n\t\t\tif (!shouldDetectSubmodules) {\n\t\t\t\tthis.logger.trace('Automatic detection of git submodules is not enabled.');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (repository.submodules.length > submodulesLimit) {\n\t\t\t\twindow.showWarningMessage(l10n.t('The \"{0}\" repository has {1} submodules which won\\'t be opened automatically. You can still open each one individually by opening a file within.', path.basename(repository.root), repository.submodules.length));\n\t\t\t\tstatusListener.dispose();\n\t\t\t}\n\n\t\t\trepository.submodules\n\t\t\t\t.slice(0, submodulesLimit)\n\t\t\t\t.map(r => path.join(repository.root, r.path))\n\t\t\t\t.forEach(p => {\n\t\t\t\t\tthis.logger.trace(`Opening submodule: '${p}'`);\n\t\t\t\t\tthis.eventuallyScanPossibleGitRepository(p);\n\t\t\t\t});\n\t\t};\n\n\t\tconst updateMergeChanges = () => {\n\t\t\t// set mergeChanges context\n\t\t\tconst mergeChanges: Uri[] = [];\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tfor (const state of repository.mergeGroup.resourceStates) {\n\t\t\t\t\tmergeChanges.push(state.resourceUri);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcommands.executeCommand('setContext', 'git.mergeChanges', mergeChanges);\n\t\t};\n\n\t\tconst statusListener = repository.onDidRunGitStatus(() => {\n\t\t\tcheckForSubmodules();\n\t\t\tupdateMergeChanges();\n\t\t});\n\t\tcheckForSubmodules();\n\n\t\tconst updateOperationInProgressContext = () => {\n\t\t\tlet operationInProgress = false;\n\t\t\tfor (const { repository } of this.openRepositories.values()) {\n\t\t\t\tif (repository.operations.shouldDisableCommands()) {\n\t\t\t\t\toperationInProgress = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcommands.executeCommand('setContext', 'operationInProgress', operationInProgress);\n\t\t};\n\n\t\tconst operationEvent = anyEvent(repository.onDidRunOperation as Event, repository.onRunOperation as Event);\n\t\tconst operationListener = operationEvent(() => updateOperationInProgressContext());\n\t\tupdateOperationInProgressContext();\n\n\t\tconst dispose = () => {\n\t\t\tdisappearListener.dispose();\n\t\t\tchangeListener.dispose();\n\t\t\toriginalResourceChangeListener.dispose();\n\t\t\tstatusListener.dispose();\n\t\t\toperationListener.dispose();\n\t\t\trepository.dispose();\n\n\t\t\tthis.openRepositories = this.openRepositories.filter(e => e !== openRepository);\n\t\t\tthis._onDidCloseRepository.fire(repository);\n\t\t};\n\n\t\tconst openRepository = { repository, dispose };\n\t\tthis.openRepositories.push(openRepository);\n\t\tupdateMergeChanges();\n\t\tthis._onDidOpenRepository.fire(repository);\n\t}\n\n\tclose(repository: Repository): void {\n\t\tconst openRepository = this.getOpenRepository(repository);\n\n\t\tif (!openRepository) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.logger.info(`Close repository: ${repository.root}`);\n\t\tthis._closedRepositoriesManager.addRepository(openRepository.repository.root);\n\n\t\topenRepository.dispose();\n\t}\n\n\tasync pickRepository(): Promise {\n\t\tif (this.openRepositories.length === 0) {\n\t\t\tthrow new Error(l10n.t('There are no available repositories'));\n\t\t}\n\n\t\tconst picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index));\n\t\tconst active = window.activeTextEditor;\n\t\tconst repository = active && this.getRepository(active.document.fileName);\n\t\tconst index = picks.findIndex(pick => pick.repository === repository);\n\n\t\t// Move repository pick containing the active text editor to appear first\n\t\tif (index > -1) {\n\t\t\tpicks.unshift(...picks.splice(index, 1));\n\t\t}\n\n\t\tconst placeHolder = l10n.t('Choose a repository');\n\t\tconst pick = await window.showQuickPick(picks, { placeHolder });\n\n\t\treturn pick && pick.repository;\n\t}\n\n\tgetRepository(sourceControl: SourceControl): Repository | undefined;\n\tgetRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;\n\tgetRepository(path: string): Repository | undefined;\n\tgetRepository(resource: Uri): Repository | undefined;\n\tgetRepository(hint: any): Repository | undefined {\n\t\tconst liveRepository = this.getOpenRepository(hint);\n\t\treturn liveRepository && liveRepository.repository;\n\t}\n\n\tprivate async getRepositoryExact(repoPath: string): Promise {\n\t\tconst repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' });\n\n\t\tfor (const openRepository of this.openRepositories) {\n\t\t\tconst rootPathCanonical = await fs.promises.realpath(openRepository.repository.root, { encoding: 'utf8' });\n\t\t\tif (pathEquals(rootPathCanonical, repoPathCanonical)) {\n\t\t\t\treturn openRepository.repository;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate getOpenRepository(repository: Repository): OpenRepository | undefined;\n\tprivate getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined;\n\tprivate getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined;\n\tprivate getOpenRepository(path: string): OpenRepository | undefined;\n\tprivate getOpenRepository(resource: Uri): OpenRepository | undefined;\n\tprivate getOpenRepository(hint: any): OpenRepository | undefined {\n\t\tif (!hint) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (hint instanceof Repository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint)[0];\n\t\t}\n\n\t\tif (hint instanceof ApiRepository) {\n\t\t\treturn this.openRepositories.filter(r => r.repository === hint.repository)[0];\n\t\t}\n\n\t\tif (typeof hint === 'string') {\n\t\t\thint = Uri.file(hint);\n\t\t}\n\n\t\tif (hint instanceof Uri) {\n\t\t\tlet resourcePath: string;\n\n\t\t\tif (hint.scheme === 'git') {\n\t\t\t\tresourcePath = fromGitUri(hint).path;\n\t\t\t} else {\n\t\t\t\tresourcePath = hint.fsPath;\n\t\t\t}\n\n\t\t\touter:\n\t\t\tfor (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) {\n\t\t\t\tif (!isDescendant(liveRepository.repository.root, resourcePath)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const submodule of liveRepository.repository.submodules) {\n\t\t\t\t\tconst submoduleRoot = path.join(liveRepository.repository.root, submodule.path);\n\n\t\t\t\t\tif (isDescendant(submoduleRoot, resourcePath)) {\n\t\t\t\t\t\tcontinue outer;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tfor (const liveRepository of this.openRepositories) {\n\t\t\tconst repository = liveRepository.repository;\n\n\t\t\tif (hint === repository.sourceControl) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\n\t\t\tif (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup || hint === repository.untrackedGroup) {\n\t\t\t\treturn liveRepository;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tgetRepositoryForSubmodule(submoduleUri: Uri): Repository | undefined {\n\t\tfor (const repository of this.repositories) {\n\t\t\tfor (const submodule of repository.submodules) {\n\t\t\t\tconst submodulePath = path.join(repository.root, submodule.path);\n\n\t\t\t\tif (submodulePath === submoduleUri.fsPath) {\n\t\t\t\t\treturn repository;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[742,747)", + "modifiedRange": "[742,751)", + "innerChanges": [ + { + "originalRange": "[742,3 -> 742,3]", + "modifiedRange": "[743,3 -> 743,8]" + }, + { + "originalRange": "[742,24 -> 742,25]", + "modifiedRange": "[743,29 -> 743,31]" + }, + { + "originalRange": "[742,47 -> 742,63]", + "modifiedRange": "[743,53 -> 743,54]" + }, + { + "originalRange": "[743,57 -> 743,58]", + "modifiedRange": "[744,57 -> 744,71]" + }, + { + "originalRange": "[744,4 -> 744,11]", + "modifiedRange": "[745,4 -> 745,8]" + }, + { + "originalRange": "[744,59 -> 745,6]", + "modifiedRange": "[745,56 -> 745,59]" + }, + { + "originalRange": "[746,24 -> 746,25]", + "modifiedRange": "[746,26 -> 746,26]" + }, + { + "originalRange": "[746,37 -> 746,37]", + "modifiedRange": "[747,4 -> 750,20]" + } + ] + }, + { + "originalRange": "[815,815)", + "modifiedRange": "[819,832)", + "innerChanges": null + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json index 8bc08d08086..61e711a47e3 100644 --- a/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json @@ -33,16 +33,8 @@ "modifiedRange": "[7,9 -> 7,27]" }, { - "originalRange": "[7,61 -> 7,105]", - "modifiedRange": "[7,50 -> 7,85]" - }, - { - "originalRange": "[7,112 -> 8,10]", - "modifiedRange": "[7,92 -> 7,130]" - }, - { - "originalRange": "[8,15 -> 10,1]", - "modifiedRange": "[7,135 -> 8,1]" + "originalRange": "[7,61 -> 10,1]", + "modifiedRange": "[7,50 -> 8,1]" } ] }, diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-1/1.tst b/src/vs/editor/test/node/diffing/fixtures/noise-1/1.tst new file mode 100644 index 00000000000..bd5594cabb4 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-1/1.tst @@ -0,0 +1,57 @@ +this._sash = derivedWithStore('sash', (reader, store) => { + const showSash = this._options.renderSideBySide.read(reader); + this.elements.root.classList.toggle('side-by-side', showSash); + if (!showSash) { return undefined; } + const result = store.add(new DiffEditorSash( + this._options, + this.elements.root, + { + height: this._rootSizeObserver.height, + width: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)), + } + )); + store.add(autorun('setBoundarySashes', reader => { + const boundarySashes = this._boundarySashes.read(reader); + if (boundarySashes) { + result.setBoundarySashes(boundarySashes); + } + })); + return result; +}); +this._register(keepAlive(this._sash, true)); + +this._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => { + this.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options)); +})); + +this._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => { + store.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options)); +})); +this._register(autorunWithStore2('ViewZoneManager', (reader, store) => { + store.add(this._instantiationService.createInstance( + readHotReloadableExport(ViewZoneManager, reader), + this._editors, + this._diffModel, + this._options, + this, + () => this.unchangedRangesFeature.isUpdatingViewZones, + )); +})); + +this._register(autorunWithStore2('OverviewRulerPart', (reader, store) => { + store.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors, + this.elements.root, + this._diffModel, + this._rootSizeObserver.width, + this._rootSizeObserver.height, + this._layoutInfo.map(i => i.modifiedEditor), + this._options, + )); +})); + +this._reviewPane = this._register(this._instantiationService.createInstance(DiffReview2, this)); +this.elements.root.appendChild(this._reviewPane.domNode.domNode); +this.elements.root.appendChild(this._reviewPane.actionBarContainer.domNode); +reviewPaneObservable.set(this._reviewPane, undefined); + +this._createDiffEditorContributions(); diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-1/2.tst b/src/vs/editor/test/node/diffing/fixtures/noise-1/2.tst new file mode 100644 index 00000000000..0432b64d75f --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-1/2.tst @@ -0,0 +1,67 @@ +this._sash = derivedWithStore('sash', (reader, store) => { + const showSash = this._options.renderSideBySide.read(reader); + this.elements.root.classList.toggle('side-by-side', showSash); + if (!showSash) { return undefined; } + const result = store.add(new DiffEditorSash( + this._options, + this.elements.root, + { + height: this._rootSizeObserver.height, + width: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)), + } + )); + store.add(autorun('setBoundarySashes', reader => { + const boundarySashes = this._boundarySashes.read(reader); + if (boundarySashes) { + result.setBoundarySashes(boundarySashes); + } + })); + return result; +}); +this._register(keepAlive(this._sash, true)); + +this._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => { + this.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options)); +})); + +this._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => { + store.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options)); +})); +this._register(autorunWithStore2('ViewZoneManager', (reader, store) => { + store.add(this._instantiationService.createInstance( + readHotReloadableExport(ViewZoneManager, reader), + this._editors, + this._diffModel, + this._options, + this, + () => this.unchangedRangesFeature.isUpdatingViewZones, + )); +})); + +this._register(autorunWithStore2('OverviewRulerPart', (reader, store) => { + store.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors, + this.elements.root, + this._diffModel, + this._rootSizeObserver.width, + this._rootSizeObserver.height, + this._layoutInfo.map(i => i.modifiedEditor), + this._options, + )); +})); + +this._register(autorunWithStore2('_accessibleDiffViewer', (reader, store) => { + this._accessibleDiffViewer = store.add(this._register(this._instantiationService.createInstance( + readHotReloadableExport(AccessibleDiffViewer, reader), + this.elements.accessibleDiffViewer, + this._accessibleDiffViewerVisible, + this._rootSizeObserver.width, + this._rootSizeObserver.height, + this._diffModel.map((m, r) => m?.diff.read(r)?.mappings.map(m => m.lineRangeMapping)), + this._editors, + ))); +})); +const visibility = this._accessibleDiffViewerVisible.map(v => v ? 'hidden' : 'visible'); +this._register(applyStyle(this.elements.modified, { visibility })); +this._register(applyStyle(this.elements.original, { visibility })); + +this._createDiffEditorContributions(); diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json new file mode 100644 index 00000000000..5a679831447 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json @@ -0,0 +1,30 @@ +{ + "original": { + "content": "this._sash = derivedWithStore('sash', (reader, store) => {\n\tconst showSash = this._options.renderSideBySide.read(reader);\n\tthis.elements.root.classList.toggle('side-by-side', showSash);\n\tif (!showSash) { return undefined; }\n\tconst result = store.add(new DiffEditorSash(\n\t\tthis._options,\n\t\tthis.elements.root,\n\t\t{\n\t\t\theight: this._rootSizeObserver.height,\n\t\t\twidth: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),\n\t\t}\n\t));\n\tstore.add(autorun('setBoundarySashes', reader => {\n\t\tconst boundarySashes = this._boundarySashes.read(reader);\n\t\tif (boundarySashes) {\n\t\t\tresult.setBoundarySashes(boundarySashes);\n\t\t}\n\t}));\n\treturn result;\n});\nthis._register(keepAlive(this._sash, true));\n\nthis._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => {\n\tthis.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options));\n}));\n\nthis._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => {\n\tstore.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options));\n}));\nthis._register(autorunWithStore2('ViewZoneManager', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(\n\t\treadHotReloadableExport(ViewZoneManager, reader),\n\t\tthis._editors,\n\t\tthis._diffModel,\n\t\tthis._options,\n\t\tthis,\n\t\t() => this.unchangedRangesFeature.isUpdatingViewZones,\n\t));\n}));\n\nthis._register(autorunWithStore2('OverviewRulerPart', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors,\n\t\tthis.elements.root,\n\t\tthis._diffModel,\n\t\tthis._rootSizeObserver.width,\n\t\tthis._rootSizeObserver.height,\n\t\tthis._layoutInfo.map(i => i.modifiedEditor),\n\t\tthis._options,\n\t));\n}));\n\nthis._reviewPane = this._register(this._instantiationService.createInstance(DiffReview2, this));\nthis.elements.root.appendChild(this._reviewPane.domNode.domNode);\nthis.elements.root.appendChild(this._reviewPane.actionBarContainer.domNode);\nreviewPaneObservable.set(this._reviewPane, undefined);\n\nthis._createDiffEditorContributions();\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "this._sash = derivedWithStore('sash', (reader, store) => {\n\tconst showSash = this._options.renderSideBySide.read(reader);\n\tthis.elements.root.classList.toggle('side-by-side', showSash);\n\tif (!showSash) { return undefined; }\n\tconst result = store.add(new DiffEditorSash(\n\t\tthis._options,\n\t\tthis.elements.root,\n\t\t{\n\t\t\theight: this._rootSizeObserver.height,\n\t\t\twidth: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),\n\t\t}\n\t));\n\tstore.add(autorun('setBoundarySashes', reader => {\n\t\tconst boundarySashes = this._boundarySashes.read(reader);\n\t\tif (boundarySashes) {\n\t\t\tresult.setBoundarySashes(boundarySashes);\n\t\t}\n\t}));\n\treturn result;\n});\nthis._register(keepAlive(this._sash, true));\n\nthis._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => {\n\tthis.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options));\n}));\n\nthis._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => {\n\tstore.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options));\n}));\nthis._register(autorunWithStore2('ViewZoneManager', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(\n\t\treadHotReloadableExport(ViewZoneManager, reader),\n\t\tthis._editors,\n\t\tthis._diffModel,\n\t\tthis._options,\n\t\tthis,\n\t\t() => this.unchangedRangesFeature.isUpdatingViewZones,\n\t));\n}));\n\nthis._register(autorunWithStore2('OverviewRulerPart', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors,\n\t\tthis.elements.root,\n\t\tthis._diffModel,\n\t\tthis._rootSizeObserver.width,\n\t\tthis._rootSizeObserver.height,\n\t\tthis._layoutInfo.map(i => i.modifiedEditor),\n\t\tthis._options,\n\t));\n}));\n\nthis._register(autorunWithStore2('_accessibleDiffViewer', (reader, store) => {\n\tthis._accessibleDiffViewer = store.add(this._register(this._instantiationService.createInstance(\n\t\treadHotReloadableExport(AccessibleDiffViewer, reader),\n\t\tthis.elements.accessibleDiffViewer,\n\t\tthis._accessibleDiffViewerVisible,\n\t\tthis._rootSizeObserver.width,\n\t\tthis._rootSizeObserver.height,\n\t\tthis._diffModel.map((m, r) => m?.diff.read(r)?.mappings.map(m => m.lineRangeMapping)),\n\t\tthis._editors,\n\t)));\n}));\nconst visibility = this._accessibleDiffViewerVisible.map(v => v ? 'hidden' : 'visible');\nthis._register(applyStyle(this.elements.modified, { visibility }));\nthis._register(applyStyle(this.elements.original, { visibility }));\n\nthis._createDiffEditorContributions();\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[52,56)", + "modifiedRange": "[52,66)", + "innerChanges": [ + { + "originalRange": "[52,7 -> 52,20]", + "modifiedRange": "[52,7 -> 53,2]" + }, + { + "originalRange": "[52,24 -> 52,24]", + "modifiedRange": "[53,6 -> 53,45]" + }, + { + "originalRange": "[52,77 -> 55,53]", + "modifiedRange": "[53,98 -> 65,66]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-1/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/noise-1/legacy.expected.diff.json new file mode 100644 index 00000000000..65a39f37dbe --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-1/legacy.expected.diff.json @@ -0,0 +1,70 @@ +{ + "original": { + "content": "this._sash = derivedWithStore('sash', (reader, store) => {\n\tconst showSash = this._options.renderSideBySide.read(reader);\n\tthis.elements.root.classList.toggle('side-by-side', showSash);\n\tif (!showSash) { return undefined; }\n\tconst result = store.add(new DiffEditorSash(\n\t\tthis._options,\n\t\tthis.elements.root,\n\t\t{\n\t\t\theight: this._rootSizeObserver.height,\n\t\t\twidth: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),\n\t\t}\n\t));\n\tstore.add(autorun('setBoundarySashes', reader => {\n\t\tconst boundarySashes = this._boundarySashes.read(reader);\n\t\tif (boundarySashes) {\n\t\t\tresult.setBoundarySashes(boundarySashes);\n\t\t}\n\t}));\n\treturn result;\n});\nthis._register(keepAlive(this._sash, true));\n\nthis._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => {\n\tthis.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options));\n}));\n\nthis._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => {\n\tstore.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options));\n}));\nthis._register(autorunWithStore2('ViewZoneManager', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(\n\t\treadHotReloadableExport(ViewZoneManager, reader),\n\t\tthis._editors,\n\t\tthis._diffModel,\n\t\tthis._options,\n\t\tthis,\n\t\t() => this.unchangedRangesFeature.isUpdatingViewZones,\n\t));\n}));\n\nthis._register(autorunWithStore2('OverviewRulerPart', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors,\n\t\tthis.elements.root,\n\t\tthis._diffModel,\n\t\tthis._rootSizeObserver.width,\n\t\tthis._rootSizeObserver.height,\n\t\tthis._layoutInfo.map(i => i.modifiedEditor),\n\t\tthis._options,\n\t));\n}));\n\nthis._reviewPane = this._register(this._instantiationService.createInstance(DiffReview2, this));\nthis.elements.root.appendChild(this._reviewPane.domNode.domNode);\nthis.elements.root.appendChild(this._reviewPane.actionBarContainer.domNode);\nreviewPaneObservable.set(this._reviewPane, undefined);\n\nthis._createDiffEditorContributions();\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "this._sash = derivedWithStore('sash', (reader, store) => {\n\tconst showSash = this._options.renderSideBySide.read(reader);\n\tthis.elements.root.classList.toggle('side-by-side', showSash);\n\tif (!showSash) { return undefined; }\n\tconst result = store.add(new DiffEditorSash(\n\t\tthis._options,\n\t\tthis.elements.root,\n\t\t{\n\t\t\theight: this._rootSizeObserver.height,\n\t\t\twidth: this._rootSizeObserver.width.map((w, reader) => w - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),\n\t\t}\n\t));\n\tstore.add(autorun('setBoundarySashes', reader => {\n\t\tconst boundarySashes = this._boundarySashes.read(reader);\n\t\tif (boundarySashes) {\n\t\t\tresult.setBoundarySashes(boundarySashes);\n\t\t}\n\t}));\n\treturn result;\n});\nthis._register(keepAlive(this._sash, true));\n\nthis._register(autorunWithStore2('UnchangedRangesFeature', (reader, store) => {\n\tthis.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options));\n}));\n\nthis._register(autorunWithStore2('DiffEditorDecorations', (reader, store) => {\n\tstore.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options));\n}));\nthis._register(autorunWithStore2('ViewZoneManager', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(\n\t\treadHotReloadableExport(ViewZoneManager, reader),\n\t\tthis._editors,\n\t\tthis._diffModel,\n\t\tthis._options,\n\t\tthis,\n\t\t() => this.unchangedRangesFeature.isUpdatingViewZones,\n\t));\n}));\n\nthis._register(autorunWithStore2('OverviewRulerPart', (reader, store) => {\n\tstore.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors,\n\t\tthis.elements.root,\n\t\tthis._diffModel,\n\t\tthis._rootSizeObserver.width,\n\t\tthis._rootSizeObserver.height,\n\t\tthis._layoutInfo.map(i => i.modifiedEditor),\n\t\tthis._options,\n\t));\n}));\n\nthis._register(autorunWithStore2('_accessibleDiffViewer', (reader, store) => {\n\tthis._accessibleDiffViewer = store.add(this._register(this._instantiationService.createInstance(\n\t\treadHotReloadableExport(AccessibleDiffViewer, reader),\n\t\tthis.elements.accessibleDiffViewer,\n\t\tthis._accessibleDiffViewerVisible,\n\t\tthis._rootSizeObserver.width,\n\t\tthis._rootSizeObserver.height,\n\t\tthis._diffModel.map((m, r) => m?.diff.read(r)?.mappings.map(m => m.lineRangeMapping)),\n\t\tthis._editors,\n\t)));\n}));\nconst visibility = this._accessibleDiffViewerVisible.map(v => v ? 'hidden' : 'visible');\nthis._register(applyStyle(this.elements.modified, { visibility }));\nthis._register(applyStyle(this.elements.original, { visibility }));\n\nthis._createDiffEditorContributions();\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[52,56)", + "modifiedRange": "[52,66)", + "innerChanges": [ + { + "originalRange": "[52,9 -> 52,20]", + "modifiedRange": "[52,9 -> 53,41]" + }, + { + "originalRange": "[52,77 -> 52,77]", + "modifiedRange": "[53,98 -> 54,37]" + }, + { + "originalRange": "[52,81 -> 52,84]", + "modifiedRange": "[54,41 -> 54,42]" + }, + { + "originalRange": "[52,87 -> 53,1]", + "modifiedRange": "[54,45 -> 55,3]" + }, + { + "originalRange": "[53,15 -> 53,32]", + "modifiedRange": "[55,17 -> 56,3]" + }, + { + "originalRange": "[53,38 -> 53,41]", + "modifiedRange": "[56,9 -> 56,24]" + }, + { + "originalRange": "[53,44 -> 54,1]", + "modifiedRange": "[56,27 -> 59,3]" + }, + { + "originalRange": "[54,6 -> 54,20]", + "modifiedRange": "[59,8 -> 59,80]" + }, + { + "originalRange": "[54,23 -> 54,32]", + "modifiedRange": "[59,83 -> 63,20]" + }, + { + "originalRange": "[54,38 -> 54,41]", + "modifiedRange": "[63,26 -> 63,41]" + }, + { + "originalRange": "[54,44 -> 54,75]", + "modifiedRange": "[63,44 -> 63,111]" + }, + { + "originalRange": "[55,1 -> 55,26]", + "modifiedRange": "[64,1 -> 65,1]" + }, + { + "originalRange": "[55,34 -> 55,53]", + "modifiedRange": "[65,9 -> 65,66]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-2/1.tst b/src/vs/editor/test/node/diffing/fixtures/noise-2/1.tst new file mode 100644 index 00000000000..684409b3bbb --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-2/1.tst @@ -0,0 +1,91 @@ + +const maxPersistedSessions = 25; + +export class ChatService extends Disposable implements IChatService { + + private async _sendRequestAsync(model: ChatModel, provider: IChatProvider, message: string | IChatReplyFollowup, usedSlashCommand?: ISlashCommand): Promise { + const request = model.addRequest(message); + + const resolvedCommand = typeof message === 'string' && message.startsWith('/') ? await this.handleSlashCommand(model.sessionId, message) : message; + + let gotProgress = false; + const requestType = typeof message === 'string' ? + (message.startsWith('/') ? 'slashCommand' : 'string') : + 'followup'; + + const rawResponsePromise = createCancelablePromise(async token => { + const progressCallback = (progress: IChatProgress) => { + if (token.isCancellationRequested) { + return; + } + + gotProgress = true; + if ('content' in progress) { + this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`); + } else { + this.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`); + } + + model.acceptResponseProgress(request, progress); + }; + + const stopWatch = new StopWatch(false); + token.onCancellationRequested(() => { + this.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`); + this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { + providerId: provider.id, + timeToFirstProgress: -1, + // Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling + totalTime: stopWatch.elapsed(), + result: 'cancelled', + requestType, + slashCommand: usedSlashCommand?.command + }); + + model.cancelRequest(request); + }); + if (usedSlashCommand?.command) { + this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } + let rawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token); + if (token.isCancellationRequested) { + return; + } else { + if (!rawResponse) { + this.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`); + rawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', "Provider returned null response") } }; + } + + const result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' : + rawResponse.errorDetails && gotProgress ? 'errorWithOutput' : + rawResponse.errorDetails ? 'error' : + 'success'; + this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { + providerId: provider.id, + timeToFirstProgress: rawResponse.timings?.firstProgress ?? 0, + totalTime: rawResponse.timings?.totalElapsed ?? 0, + result, + requestType, + slashCommand: usedSlashCommand?.command + }); + model.setResponse(request, rawResponse); + this.trace('sendRequest', `Provider returned response for session ${model.sessionId}`); + + // TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593 + if (provider.provideFollowups) { + Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { + model.setFollowups(request, withNullAsUndefined(followups)); + model.completeResponse(request); + }); + } else { + model.completeResponse(request); + } + } + }); + this._pendingRequests.set(model.sessionId, rawResponsePromise); + rawResponsePromise.finally(() => { + this._pendingRequests.delete(model.sessionId); + }); + return rawResponsePromise; + } +} diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-2/2.tst b/src/vs/editor/test/node/diffing/fixtures/noise-2/2.tst new file mode 100644 index 00000000000..b7778d336a9 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-2/2.tst @@ -0,0 +1,111 @@ + +const maxPersistedSessions = 25; + +export class ChatService extends Disposable implements IChatService { + + private async _sendRequestAsync(model: ChatModel, provider: IChatProvider, message: string | IChatReplyFollowup, usedSlashCommand?: ISlashCommand): Promise { + const request = model.addRequest(message); + + const resolvedCommand = typeof message === 'string' && message.startsWith('/') ? await this.handleSlashCommand(model.sessionId, message) : message; + + let gotProgress = false; + const requestType = typeof message === 'string' ? + (message.startsWith('/') ? 'slashCommand' : 'string') : + 'followup'; + + const rawResponsePromise = createCancelablePromise(async token => { + const progressCallback = (progress: IChatProgress) => { + if (token.isCancellationRequested) { + return; + } + + gotProgress = true; + if ('content' in progress) { + this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`); + } else { + this.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`); + } + + model.acceptResponseProgress(request, progress); + }; + + const stopWatch = new StopWatch(false); + token.onCancellationRequested(() => { + this.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`); + this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { + providerId: provider.id, + timeToFirstProgress: -1, + // Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling + totalTime: stopWatch.elapsed(), + result: 'cancelled', + requestType, + slashCommand: usedSlashCommand?.command + }); + + model.cancelRequest(request); + }); + if (usedSlashCommand?.command) { + this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } + + let rawResponse: IChatResponse | null | undefined; + + if ((typeof resolvedCommand === 'string' && typeof message === 'string' && this.chatSlashCommandService.hasCommand(resolvedCommand))) { + // contributed slash commands + // TODO: spell this out in the UI + const history: IChatMessage[] = []; + for (const request of model.getRequests()) { + if (typeof request.message !== 'string' || !request.response) { + continue; + } + history.push({ role: ChatMessageRole.User, content: request.message }); + history.push({ role: ChatMessageRole.Assistant, content: request.response?.response.value }); + } + await this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => progressCallback(p)), history, token); + rawResponse = { session: model.session! }; + + } else { + rawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token); + } + + if (token.isCancellationRequested) { + return; + } else { + if (!rawResponse) { + this.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`); + rawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', "Provider returned null response") } }; + } + + const result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' : + rawResponse.errorDetails && gotProgress ? 'errorWithOutput' : + rawResponse.errorDetails ? 'error' : + 'success'; + this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { + providerId: provider.id, + timeToFirstProgress: rawResponse.timings?.firstProgress ?? 0, + totalTime: rawResponse.timings?.totalElapsed ?? 0, + result, + requestType, + slashCommand: usedSlashCommand?.command + }); + model.setResponse(request, rawResponse); + this.trace('sendRequest', `Provider returned response for session ${model.sessionId}`); + + // TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593 + if (provider.provideFollowups) { + Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { + model.setFollowups(request, withNullAsUndefined(followups)); + model.completeResponse(request); + }); + } else { + model.completeResponse(request); + } + } + }); + this._pendingRequests.set(model.sessionId, rawResponsePromise); + rawResponsePromise.finally(() => { + this._pendingRequests.delete(model.sessionId); + }); + return rawResponsePromise; + } +} diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-2/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/noise-2/advanced.expected.diff.json new file mode 100644 index 00000000000..a30c65cb9ee --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-2/advanced.expected.diff.json @@ -0,0 +1,30 @@ +{ + "original": { + "content": "\nconst maxPersistedSessions = 25;\n\nexport class ChatService extends Disposable implements IChatService {\n\n\tprivate async _sendRequestAsync(model: ChatModel, provider: IChatProvider, message: string | IChatReplyFollowup, usedSlashCommand?: ISlashCommand): Promise {\n\t\tconst request = model.addRequest(message);\n\n\t\tconst resolvedCommand = typeof message === 'string' && message.startsWith('/') ? await this.handleSlashCommand(model.sessionId, message) : message;\n\n\t\tlet gotProgress = false;\n\t\tconst requestType = typeof message === 'string' ?\n\t\t\t(message.startsWith('/') ? 'slashCommand' : 'string') :\n\t\t\t'followup';\n\n\t\tconst rawResponsePromise = createCancelablePromise(async token => {\n\t\t\tconst progressCallback = (progress: IChatProgress) => {\n\t\t\t\tif (token.isCancellationRequested) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tgotProgress = true;\n\t\t\t\tif ('content' in progress) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`);\n\t\t\t\t} else {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`);\n\t\t\t\t}\n\n\t\t\t\tmodel.acceptResponseProgress(request, progress);\n\t\t\t};\n\n\t\t\tconst stopWatch = new StopWatch(false);\n\t\t\ttoken.onCancellationRequested(() => {\n\t\t\t\tthis.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`);\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: -1,\n\t\t\t\t\t// Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling\n\t\t\t\t\ttotalTime: stopWatch.elapsed(),\n\t\t\t\t\tresult: 'cancelled',\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\n\t\t\t\tmodel.cancelRequest(request);\n\t\t\t});\n\t\t\tif (usedSlashCommand?.command) {\n\t\t\t\tthis._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });\n\t\t\t}\n\t\t\tlet rawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token);\n\t\t\tif (token.isCancellationRequested) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tif (!rawResponse) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`);\n\t\t\t\t\trawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', \"Provider returned null response\") } };\n\t\t\t\t}\n\n\t\t\t\tconst result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' :\n\t\t\t\t\trawResponse.errorDetails && gotProgress ? 'errorWithOutput' :\n\t\t\t\t\t\trawResponse.errorDetails ? 'error' :\n\t\t\t\t\t\t\t'success';\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: rawResponse.timings?.firstProgress ?? 0,\n\t\t\t\t\ttotalTime: rawResponse.timings?.totalElapsed ?? 0,\n\t\t\t\t\tresult,\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\t\t\t\tmodel.setResponse(request, rawResponse);\n\t\t\t\tthis.trace('sendRequest', `Provider returned response for session ${model.sessionId}`);\n\n\t\t\t\t// TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593\n\t\t\t\tif (provider.provideFollowups) {\n\t\t\t\t\tPromise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => {\n\t\t\t\t\t\tmodel.setFollowups(request, withNullAsUndefined(followups));\n\t\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis._pendingRequests.set(model.sessionId, rawResponsePromise);\n\t\trawResponsePromise.finally(() => {\n\t\t\tthis._pendingRequests.delete(model.sessionId);\n\t\t});\n\t\treturn rawResponsePromise;\n\t}\n}\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "\nconst maxPersistedSessions = 25;\n\nexport class ChatService extends Disposable implements IChatService {\n\n\tprivate async _sendRequestAsync(model: ChatModel, provider: IChatProvider, message: string | IChatReplyFollowup, usedSlashCommand?: ISlashCommand): Promise {\n\t\tconst request = model.addRequest(message);\n\n\t\tconst resolvedCommand = typeof message === 'string' && message.startsWith('/') ? await this.handleSlashCommand(model.sessionId, message) : message;\n\n\t\tlet gotProgress = false;\n\t\tconst requestType = typeof message === 'string' ?\n\t\t\t(message.startsWith('/') ? 'slashCommand' : 'string') :\n\t\t\t'followup';\n\n\t\tconst rawResponsePromise = createCancelablePromise(async token => {\n\t\t\tconst progressCallback = (progress: IChatProgress) => {\n\t\t\t\tif (token.isCancellationRequested) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tgotProgress = true;\n\t\t\t\tif ('content' in progress) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`);\n\t\t\t\t} else {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`);\n\t\t\t\t}\n\n\t\t\t\tmodel.acceptResponseProgress(request, progress);\n\t\t\t};\n\n\t\t\tconst stopWatch = new StopWatch(false);\n\t\t\ttoken.onCancellationRequested(() => {\n\t\t\t\tthis.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`);\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: -1,\n\t\t\t\t\t// Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling\n\t\t\t\t\ttotalTime: stopWatch.elapsed(),\n\t\t\t\t\tresult: 'cancelled',\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\n\t\t\t\tmodel.cancelRequest(request);\n\t\t\t});\n\t\t\tif (usedSlashCommand?.command) {\n\t\t\t\tthis._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });\n\t\t\t}\n\n\t\t\tlet rawResponse: IChatResponse | null | undefined;\n\n\t\t\tif ((typeof resolvedCommand === 'string' && typeof message === 'string' && this.chatSlashCommandService.hasCommand(resolvedCommand))) {\n\t\t\t\t// contributed slash commands\n\t\t\t\t// TODO: spell this out in the UI\n\t\t\t\tconst history: IChatMessage[] = [];\n\t\t\t\tfor (const request of model.getRequests()) {\n\t\t\t\t\tif (typeof request.message !== 'string' || !request.response) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\thistory.push({ role: ChatMessageRole.User, content: request.message });\n\t\t\t\t\thistory.push({ role: ChatMessageRole.Assistant, content: request.response?.response.value });\n\t\t\t\t}\n\t\t\t\tawait this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => progressCallback(p)), history, token);\n\t\t\t\trawResponse = { session: model.session! };\n\n\t\t\t} else {\n\t\t\t\trawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token);\n\t\t\t}\n\n\t\t\tif (token.isCancellationRequested) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tif (!rawResponse) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`);\n\t\t\t\t\trawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', \"Provider returned null response\") } };\n\t\t\t\t}\n\n\t\t\t\tconst result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' :\n\t\t\t\t\trawResponse.errorDetails && gotProgress ? 'errorWithOutput' :\n\t\t\t\t\t\trawResponse.errorDetails ? 'error' :\n\t\t\t\t\t\t\t'success';\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: rawResponse.timings?.firstProgress ?? 0,\n\t\t\t\t\ttotalTime: rawResponse.timings?.totalElapsed ?? 0,\n\t\t\t\t\tresult,\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\t\t\t\tmodel.setResponse(request, rawResponse);\n\t\t\t\tthis.trace('sendRequest', `Provider returned response for session ${model.sessionId}`);\n\n\t\t\t\t// TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593\n\t\t\t\tif (provider.provideFollowups) {\n\t\t\t\t\tPromise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => {\n\t\t\t\t\t\tmodel.setFollowups(request, withNullAsUndefined(followups));\n\t\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis._pendingRequests.set(model.sessionId, rawResponsePromise);\n\t\trawResponsePromise.finally(() => {\n\t\t\tthis._pendingRequests.delete(model.sessionId);\n\t\t});\n\t\treturn rawResponsePromise;\n\t}\n}\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[50,51)", + "modifiedRange": "[50,71)", + "innerChanges": [ + { + "originalRange": "[50,1 -> 50,1]", + "modifiedRange": "[50,1 -> 51,1]" + }, + { + "originalRange": "[50,19 -> 50,27]", + "modifiedRange": "[51,19 -> 68,24]" + }, + { + "originalRange": "[51,1 -> 51,1]", + "modifiedRange": "[69,1 -> 71,1]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-2/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/noise-2/legacy.expected.diff.json new file mode 100644 index 00000000000..1c4aecc9899 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/noise-2/legacy.expected.diff.json @@ -0,0 +1,17 @@ +{ + "original": { + "content": "\nconst maxPersistedSessions = 25;\n\nexport class ChatService extends Disposable implements IChatService {\n\n\tprivate async _sendRequestAsync(model: ChatModel, provider: IChatProvider, message: string | IChatReplyFollowup, usedSlashCommand?: ISlashCommand): Promise {\n\t\tconst request = model.addRequest(message);\n\n\t\tconst resolvedCommand = typeof message === 'string' && message.startsWith('/') ? await this.handleSlashCommand(model.sessionId, message) : message;\n\n\t\tlet gotProgress = false;\n\t\tconst requestType = typeof message === 'string' ?\n\t\t\t(message.startsWith('/') ? 'slashCommand' : 'string') :\n\t\t\t'followup';\n\n\t\tconst rawResponsePromise = createCancelablePromise(async token => {\n\t\t\tconst progressCallback = (progress: IChatProgress) => {\n\t\t\t\tif (token.isCancellationRequested) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tgotProgress = true;\n\t\t\t\tif ('content' in progress) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`);\n\t\t\t\t} else {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`);\n\t\t\t\t}\n\n\t\t\t\tmodel.acceptResponseProgress(request, progress);\n\t\t\t};\n\n\t\t\tconst stopWatch = new StopWatch(false);\n\t\t\ttoken.onCancellationRequested(() => {\n\t\t\t\tthis.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`);\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: -1,\n\t\t\t\t\t// Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling\n\t\t\t\t\ttotalTime: stopWatch.elapsed(),\n\t\t\t\t\tresult: 'cancelled',\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\n\t\t\t\tmodel.cancelRequest(request);\n\t\t\t});\n\t\t\tif (usedSlashCommand?.command) {\n\t\t\t\tthis._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });\n\t\t\t}\n\t\t\tlet rawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token);\n\t\t\tif (token.isCancellationRequested) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tif (!rawResponse) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`);\n\t\t\t\t\trawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', \"Provider returned null response\") } };\n\t\t\t\t}\n\n\t\t\t\tconst result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' :\n\t\t\t\t\trawResponse.errorDetails && gotProgress ? 'errorWithOutput' :\n\t\t\t\t\t\trawResponse.errorDetails ? 'error' :\n\t\t\t\t\t\t\t'success';\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: rawResponse.timings?.firstProgress ?? 0,\n\t\t\t\t\ttotalTime: rawResponse.timings?.totalElapsed ?? 0,\n\t\t\t\t\tresult,\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\t\t\t\tmodel.setResponse(request, rawResponse);\n\t\t\t\tthis.trace('sendRequest', `Provider returned response for session ${model.sessionId}`);\n\n\t\t\t\t// TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593\n\t\t\t\tif (provider.provideFollowups) {\n\t\t\t\t\tPromise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => {\n\t\t\t\t\t\tmodel.setFollowups(request, withNullAsUndefined(followups));\n\t\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis._pendingRequests.set(model.sessionId, rawResponsePromise);\n\t\trawResponsePromise.finally(() => {\n\t\t\tthis._pendingRequests.delete(model.sessionId);\n\t\t});\n\t\treturn rawResponsePromise;\n\t}\n}\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "\nconst maxPersistedSessions = 25;\n\nexport class ChatService extends Disposable implements IChatService {\n\n\tprivate async _sendRequestAsync(model: ChatModel, provider: IChatProvider, message: string | IChatReplyFollowup, usedSlashCommand?: ISlashCommand): Promise {\n\t\tconst request = model.addRequest(message);\n\n\t\tconst resolvedCommand = typeof message === 'string' && message.startsWith('/') ? await this.handleSlashCommand(model.sessionId, message) : message;\n\n\t\tlet gotProgress = false;\n\t\tconst requestType = typeof message === 'string' ?\n\t\t\t(message.startsWith('/') ? 'slashCommand' : 'string') :\n\t\t\t'followup';\n\n\t\tconst rawResponsePromise = createCancelablePromise(async token => {\n\t\t\tconst progressCallback = (progress: IChatProgress) => {\n\t\t\t\tif (token.isCancellationRequested) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tgotProgress = true;\n\t\t\t\tif ('content' in progress) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`);\n\t\t\t\t} else {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`);\n\t\t\t\t}\n\n\t\t\t\tmodel.acceptResponseProgress(request, progress);\n\t\t\t};\n\n\t\t\tconst stopWatch = new StopWatch(false);\n\t\t\ttoken.onCancellationRequested(() => {\n\t\t\t\tthis.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`);\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: -1,\n\t\t\t\t\t// Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling\n\t\t\t\t\ttotalTime: stopWatch.elapsed(),\n\t\t\t\t\tresult: 'cancelled',\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\n\t\t\t\tmodel.cancelRequest(request);\n\t\t\t});\n\t\t\tif (usedSlashCommand?.command) {\n\t\t\t\tthis._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });\n\t\t\t}\n\n\t\t\tlet rawResponse: IChatResponse | null | undefined;\n\n\t\t\tif ((typeof resolvedCommand === 'string' && typeof message === 'string' && this.chatSlashCommandService.hasCommand(resolvedCommand))) {\n\t\t\t\t// contributed slash commands\n\t\t\t\t// TODO: spell this out in the UI\n\t\t\t\tconst history: IChatMessage[] = [];\n\t\t\t\tfor (const request of model.getRequests()) {\n\t\t\t\t\tif (typeof request.message !== 'string' || !request.response) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\thistory.push({ role: ChatMessageRole.User, content: request.message });\n\t\t\t\t\thistory.push({ role: ChatMessageRole.Assistant, content: request.response?.response.value });\n\t\t\t\t}\n\t\t\t\tawait this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => progressCallback(p)), history, token);\n\t\t\t\trawResponse = { session: model.session! };\n\n\t\t\t} else {\n\t\t\t\trawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token);\n\t\t\t}\n\n\t\t\tif (token.isCancellationRequested) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tif (!rawResponse) {\n\t\t\t\t\tthis.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`);\n\t\t\t\t\trawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', \"Provider returned null response\") } };\n\t\t\t\t}\n\n\t\t\t\tconst result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' :\n\t\t\t\t\trawResponse.errorDetails && gotProgress ? 'errorWithOutput' :\n\t\t\t\t\t\trawResponse.errorDetails ? 'error' :\n\t\t\t\t\t\t\t'success';\n\t\t\t\tthis.telemetryService.publicLog2('interactiveSessionProviderInvoked', {\n\t\t\t\t\tproviderId: provider.id,\n\t\t\t\t\ttimeToFirstProgress: rawResponse.timings?.firstProgress ?? 0,\n\t\t\t\t\ttotalTime: rawResponse.timings?.totalElapsed ?? 0,\n\t\t\t\t\tresult,\n\t\t\t\t\trequestType,\n\t\t\t\t\tslashCommand: usedSlashCommand?.command\n\t\t\t\t});\n\t\t\t\tmodel.setResponse(request, rawResponse);\n\t\t\t\tthis.trace('sendRequest', `Provider returned response for session ${model.sessionId}`);\n\n\t\t\t\t// TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593\n\t\t\t\tif (provider.provideFollowups) {\n\t\t\t\t\tPromise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => {\n\t\t\t\t\t\tmodel.setFollowups(request, withNullAsUndefined(followups));\n\t\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tmodel.completeResponse(request);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tthis._pendingRequests.set(model.sessionId, rawResponsePromise);\n\t\trawResponsePromise.finally(() => {\n\t\t\tthis._pendingRequests.delete(model.sessionId);\n\t\t});\n\t\treturn rawResponsePromise;\n\t}\n}\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[50,51)", + "modifiedRange": "[50,71)", + "innerChanges": null + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json index 85e7beaa072..92f5e20707f 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json @@ -47,20 +47,8 @@ "modifiedRange": "[8,12)", "innerChanges": [ { - "originalRange": "[11,10 -> 11,14]", - "modifiedRange": "[8,10 -> 8,11]" - }, - { - "originalRange": "[12,4 -> 13,52]", - "modifiedRange": "[9,4 -> 9,12]" - }, - { - "originalRange": "[13,55 -> 19,67]", - "modifiedRange": "[9,15 -> 9,61]" - }, - { - "originalRange": "[20,17 -> 20,25]", - "modifiedRange": "[10,17 -> 11,51]" + "originalRange": "[11,10 -> 20,25]", + "modifiedRange": "[8,10 -> 11,51]" } ] }, diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json index 07656313cdc..f639ee6bed4 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json @@ -43,12 +43,8 @@ "modifiedRange": "[14,18)", "innerChanges": [ { - "originalRange": "[14,120 -> 14,154]", - "modifiedRange": "[14,120 -> 14,162]" - }, - { - "originalRange": "[14,165 -> 14,211]", - "modifiedRange": "[14,173 -> 17,1]" + "originalRange": "[14,120 -> 14,211]", + "modifiedRange": "[14,120 -> 17,1]" } ] }, @@ -65,12 +61,8 @@ "modifiedRange": "[21,8 -> 21,43]" }, { - "originalRange": "[17,71 -> 17,72]", - "modifiedRange": "[21,74 -> 22,1]" - }, - { - "originalRange": "[18,3 -> 23,4]", - "modifiedRange": "[23,3 -> 23,120]" + "originalRange": "[17,71 -> 23,4]", + "modifiedRange": "[21,74 -> 23,120]" } ] }, @@ -89,20 +81,8 @@ "modifiedRange": "[26,38)", "innerChanges": [ { - "originalRange": "[28,1 -> 28,1]", - "modifiedRange": "[26,1 -> 27,1]" - }, - { - "originalRange": "[28,15 -> 28,20]", - "modifiedRange": "[27,15 -> 27,16]" - }, - { - "originalRange": "[29,4 -> 29,24]", - "modifiedRange": "[28,4 -> 29,36]" - }, - { - "originalRange": "[30,4 -> 30,31]", - "modifiedRange": "[30,4 -> 37,9]" + "originalRange": "[28,1 -> 30,31]", + "modifiedRange": "[26,1 -> 37,9]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json index cc5f77b5dc8..da419b4a59f 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json @@ -31,16 +31,8 @@ "modifiedRange": "[17,80)", "innerChanges": [ { - "originalRange": "[13,10 -> 13,41]", - "modifiedRange": "[17,10 -> 35,18]" - }, - { - "originalRange": "[14,2 -> 14,8]", - "modifiedRange": "[36,2 -> 39,8]" - }, - { - "originalRange": "[14,13 -> 14,43]", - "modifiedRange": "[39,13 -> 79,2]" + "originalRange": "[13,10 -> 14,43]", + "modifiedRange": "[17,10 -> 79,2]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx new file mode 100644 index 00000000000..693ebcc1e12 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx @@ -0,0 +1,16 @@ +import { Stack, Text } from '@fluentui/react'; +import { View } from '../../layout/layout'; + +export const WelcomeView = () => { + return ( + + + + + Welcome to the VS Code Tools application. + + + + + ); +} diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx new file mode 100644 index 00000000000..cb911e799d4 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx @@ -0,0 +1,20 @@ +import { Nav } from '@fluentui/react'; +import { View } from '../../layout/layout'; + +export const WelcomeView = () => { + return ( + + + + ); +} diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json new file mode 100644 index 00000000000..85a9a2fe928 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json @@ -0,0 +1,36 @@ +{ + "original": { + "content": "import { Stack, Text } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./1.tsx" + }, + "modified": { + "content": "import { Nav } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./2.tsx" + }, + "diffs": [ + { + "originalRange": "[1,2)", + "modifiedRange": "[1,2)", + "innerChanges": [ + { + "originalRange": "[1,10 -> 1,21]", + "modifiedRange": "[1,10 -> 1,13]" + } + ] + }, + { + "originalRange": "[7,14)", + "modifiedRange": "[7,18)", + "innerChanges": [ + { + "originalRange": "[7,5 -> 12,17]", + "modifiedRange": "[7,5 -> 16,7]" + }, + { + "originalRange": "[13,6 -> 13,11]", + "modifiedRange": "[17,6 -> 17,9]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json new file mode 100644 index 00000000000..c248859e098 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json @@ -0,0 +1,64 @@ +{ + "original": { + "content": "import { Stack, Text } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./1.tsx" + }, + "modified": { + "content": "import { Nav } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./2.tsx" + }, + "diffs": [ + { + "originalRange": "[1,2)", + "modifiedRange": "[1,2)", + "innerChanges": [ + { + "originalRange": "[1,10 -> 1,21]", + "modifiedRange": "[1,10 -> 1,13]" + } + ] + }, + { + "originalRange": "[7,14)", + "modifiedRange": "[7,18)", + "innerChanges": [ + { + "originalRange": "[7,5 -> 7,11]", + "modifiedRange": "[7,5 -> 8,5]" + }, + { + "originalRange": "[7,14 -> 7,43]", + "modifiedRange": "[8,8 -> 11,140]" + }, + { + "originalRange": "[8,5 -> 8,6]", + "modifiedRange": "[12,5 -> 12,25]" + }, + { + "originalRange": "[8,9 -> 9,12]", + "modifiedRange": "[12,28 -> 12,131]" + }, + { + "originalRange": "[10,7 -> 10,22]", + "modifiedRange": "[13,7 -> 13,17]" + }, + { + "originalRange": "[10,30 -> 10,48]", + "modifiedRange": "[13,25 -> 14,8]" + }, + { + "originalRange": "[11,6 -> 11,13]", + "modifiedRange": "[15,6 -> 15,7]" + }, + { + "originalRange": "[12,5 -> 12,17]", + "modifiedRange": "[16,5 -> 16,7]" + }, + { + "originalRange": "[13,6 -> 13,11]", + "modifiedRange": "[17,6 -> 17,9]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/loader.js b/src/vs/loader.js index cebfe6da858..c2d38dfca0e 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -22,60 +22,60 @@ const _amdLoaderGlobal = this; const _commonjsGlobal = typeof global === 'object' ? global : {}; var AMDLoader; (function (AMDLoader) { - AMDLoader.global = _amdLoaderGlobal; - class Environment { - get isWindows() { - this._detect(); - return this._isWindows; - } - get isNode() { - this._detect(); - return this._isNode; - } - get isElectronRenderer() { - this._detect(); - return this._isElectronRenderer; - } - get isWebWorker() { - this._detect(); - return this._isWebWorker; - } - get isElectronNodeIntegrationWebWorker() { - this._detect(); - return this._isElectronNodeIntegrationWebWorker; - } - constructor() { - this._detected = false; - this._isWindows = false; - this._isNode = false; - this._isElectronRenderer = false; - this._isWebWorker = false; - this._isElectronNodeIntegrationWebWorker = false; - } - _detect() { - if (this._detected) { - return; - } - this._detected = true; - this._isWindows = Environment._isWindows(); - this._isNode = (typeof module !== 'undefined' && !!module.exports); - this._isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer'); - this._isWebWorker = (typeof AMDLoader.global.importScripts === 'function'); - this._isElectronNodeIntegrationWebWorker = this._isWebWorker && (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'worker'); - } - static _isWindows() { - if (typeof navigator !== 'undefined') { - if (navigator.userAgent && navigator.userAgent.indexOf('Windows') >= 0) { - return true; - } - } - if (typeof process !== 'undefined') { - return (process.platform === 'win32'); - } - return false; - } - } - AMDLoader.Environment = Environment; + AMDLoader.global = _amdLoaderGlobal; + class Environment { + get isWindows() { + this._detect(); + return this._isWindows; + } + get isNode() { + this._detect(); + return this._isNode; + } + get isElectronRenderer() { + this._detect(); + return this._isElectronRenderer; + } + get isWebWorker() { + this._detect(); + return this._isWebWorker; + } + get isElectronNodeIntegrationWebWorker() { + this._detect(); + return this._isElectronNodeIntegrationWebWorker; + } + constructor() { + this._detected = false; + this._isWindows = false; + this._isNode = false; + this._isElectronRenderer = false; + this._isWebWorker = false; + this._isElectronNodeIntegrationWebWorker = false; + } + _detect() { + if (this._detected) { + return; + } + this._detected = true; + this._isWindows = Environment._isWindows(); + this._isNode = (typeof module !== 'undefined' && !!module.exports); + this._isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer'); + this._isWebWorker = (typeof AMDLoader.global.importScripts === 'function'); + this._isElectronNodeIntegrationWebWorker = this._isWebWorker && (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'worker'); + } + static _isWindows() { + if (typeof navigator !== 'undefined') { + if (navigator.userAgent && navigator.userAgent.indexOf('Windows') >= 0) { + return true; + } + } + if (typeof process !== 'undefined') { + return (process.platform === 'win32'); + } + return false; + } + } + AMDLoader.Environment = Environment; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -83,36 +83,36 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - class LoaderEvent { - constructor(type, detail, timestamp) { - this.type = type; - this.detail = detail; - this.timestamp = timestamp; - } - } - AMDLoader.LoaderEvent = LoaderEvent; - class LoaderEventRecorder { - constructor(loaderAvailableTimestamp) { - this._events = [new LoaderEvent(1 /* LoaderEventType.LoaderAvailable */, '', loaderAvailableTimestamp)]; - } - record(type, detail) { - this._events.push(new LoaderEvent(type, detail, AMDLoader.Utilities.getHighPerformanceTimestamp())); - } - getEvents() { - return this._events; - } - } - AMDLoader.LoaderEventRecorder = LoaderEventRecorder; - class NullLoaderEventRecorder { - record(type, detail) { - // Nothing to do - } - getEvents() { - return []; - } - } - NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); - AMDLoader.NullLoaderEventRecorder = NullLoaderEventRecorder; + class LoaderEvent { + constructor(type, detail, timestamp) { + this.type = type; + this.detail = detail; + this.timestamp = timestamp; + } + } + AMDLoader.LoaderEvent = LoaderEvent; + class LoaderEventRecorder { + constructor(loaderAvailableTimestamp) { + this._events = [new LoaderEvent(1 /* LoaderEventType.LoaderAvailable */, '', loaderAvailableTimestamp)]; + } + record(type, detail) { + this._events.push(new LoaderEvent(type, detail, AMDLoader.Utilities.getHighPerformanceTimestamp())); + } + getEvents() { + return this._events; + } + } + AMDLoader.LoaderEventRecorder = LoaderEventRecorder; + class NullLoaderEventRecorder { + record(type, detail) { + // Nothing to do + } + getEvents() { + return []; + } + } + NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); + AMDLoader.NullLoaderEventRecorder = NullLoaderEventRecorder; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -120,99 +120,99 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - class Utilities { - /** - * This method does not take care of / vs \ - */ - static fileUriToFilePath(isWindows, uri) { - uri = decodeURI(uri).replace(/%23/g, '#'); - if (isWindows) { - if (/^file:\/\/\//.test(uri)) { - // This is a URI without a hostname => return only the path segment - return uri.substr(8); - } - if (/^file:\/\//.test(uri)) { - return uri.substr(5); - } - } - else { - if (/^file:\/\//.test(uri)) { - return uri.substr(7); - } - } - // Not sure... - return uri; - } - static startsWith(haystack, needle) { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; - } - static endsWith(haystack, needle) { - return haystack.length >= needle.length && haystack.substr(haystack.length - needle.length) === needle; - } - // only check for "?" before "#" to ensure that there is a real Query-String - static containsQueryString(url) { - return /^[^\#]*\?/gi.test(url); - } - /** - * Does `url` start with http:// or https:// or file:// or / ? - */ - static isAbsolutePath(url) { - return /^((http:\/\/)|(https:\/\/)|(file:\/\/)|(\/))/.test(url); - } - static forEachProperty(obj, callback) { - if (obj) { - let key; - for (key in obj) { - if (obj.hasOwnProperty(key)) { - callback(key, obj[key]); - } - } - } - } - static isEmpty(obj) { - let isEmpty = true; - Utilities.forEachProperty(obj, () => { - isEmpty = false; - }); - return isEmpty; - } - static recursiveClone(obj) { - if (!obj || typeof obj !== 'object' || obj instanceof RegExp) { - return obj; - } - if (!Array.isArray(obj) && Object.getPrototypeOf(obj) !== Object.prototype) { - // only clone "simple" objects - return obj; - } - let result = Array.isArray(obj) ? [] : {}; - Utilities.forEachProperty(obj, (key, value) => { - if (value && typeof value === 'object') { - result[key] = Utilities.recursiveClone(value); - } - else { - result[key] = value; - } - }); - return result; - } - static generateAnonymousModule() { - return '===anonymous' + (Utilities.NEXT_ANONYMOUS_ID++) + '==='; - } - static isAnonymousModule(id) { - return Utilities.startsWith(id, '===anonymous'); - } - static getHighPerformanceTimestamp() { - if (!this.PERFORMANCE_NOW_PROBED) { - this.PERFORMANCE_NOW_PROBED = true; - this.HAS_PERFORMANCE_NOW = (AMDLoader.global.performance && typeof AMDLoader.global.performance.now === 'function'); - } - return (this.HAS_PERFORMANCE_NOW ? AMDLoader.global.performance.now() : Date.now()); - } - } - Utilities.NEXT_ANONYMOUS_ID = 1; - Utilities.PERFORMANCE_NOW_PROBED = false; - Utilities.HAS_PERFORMANCE_NOW = false; - AMDLoader.Utilities = Utilities; + class Utilities { + /** + * This method does not take care of / vs \ + */ + static fileUriToFilePath(isWindows, uri) { + uri = decodeURI(uri).replace(/%23/g, '#'); + if (isWindows) { + if (/^file:\/\/\//.test(uri)) { + // This is a URI without a hostname => return only the path segment + return uri.substr(8); + } + if (/^file:\/\//.test(uri)) { + return uri.substr(5); + } + } + else { + if (/^file:\/\//.test(uri)) { + return uri.substr(7); + } + } + // Not sure... + return uri; + } + static startsWith(haystack, needle) { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + } + static endsWith(haystack, needle) { + return haystack.length >= needle.length && haystack.substr(haystack.length - needle.length) === needle; + } + // only check for "?" before "#" to ensure that there is a real Query-String + static containsQueryString(url) { + return /^[^\#]*\?/gi.test(url); + } + /** + * Does `url` start with http:// or https:// or file:// or / ? + */ + static isAbsolutePath(url) { + return /^((http:\/\/)|(https:\/\/)|(file:\/\/)|(\/))/.test(url); + } + static forEachProperty(obj, callback) { + if (obj) { + let key; + for (key in obj) { + if (obj.hasOwnProperty(key)) { + callback(key, obj[key]); + } + } + } + } + static isEmpty(obj) { + let isEmpty = true; + Utilities.forEachProperty(obj, () => { + isEmpty = false; + }); + return isEmpty; + } + static recursiveClone(obj) { + if (!obj || typeof obj !== 'object' || obj instanceof RegExp) { + return obj; + } + if (!Array.isArray(obj) && Object.getPrototypeOf(obj) !== Object.prototype) { + // only clone "simple" objects + return obj; + } + let result = Array.isArray(obj) ? [] : {}; + Utilities.forEachProperty(obj, (key, value) => { + if (value && typeof value === 'object') { + result[key] = Utilities.recursiveClone(value); + } + else { + result[key] = value; + } + }); + return result; + } + static generateAnonymousModule() { + return '===anonymous' + (Utilities.NEXT_ANONYMOUS_ID++) + '==='; + } + static isAnonymousModule(id) { + return Utilities.startsWith(id, '===anonymous'); + } + static getHighPerformanceTimestamp() { + if (!this.PERFORMANCE_NOW_PROBED) { + this.PERFORMANCE_NOW_PROBED = true; + this.HAS_PERFORMANCE_NOW = (AMDLoader.global.performance && typeof AMDLoader.global.performance.now === 'function'); + } + return (this.HAS_PERFORMANCE_NOW ? AMDLoader.global.performance.now() : Date.now()); + } + } + Utilities.NEXT_ANONYMOUS_ID = 1; + Utilities.PERFORMANCE_NOW_PROBED = false; + Utilities.HAS_PERFORMANCE_NOW = false; + AMDLoader.Utilities = Utilities; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -220,318 +220,318 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - function ensureError(err) { - if (err instanceof Error) { - return err; - } - const result = new Error(err.message || String(err) || 'Unknown Error'); - if (err.stack) { - result.stack = err.stack; - } - return result; - } - AMDLoader.ensureError = ensureError; - ; - class ConfigurationOptionsUtil { - /** - * Ensure configuration options make sense - */ - static validateConfigurationOptions(options) { - function defaultOnError(err) { - if (err.phase === 'loading') { - console.error('Loading "' + err.moduleId + '" failed'); - console.error(err); - console.error('Here are the modules that depend on it:'); - console.error(err.neededBy); - return; - } - if (err.phase === 'factory') { - console.error('The factory function of "' + err.moduleId + '" has thrown an exception'); - console.error(err); - console.error('Here are the modules that depend on it:'); - console.error(err.neededBy); - return; - } - } - options = options || {}; - if (typeof options.baseUrl !== 'string') { - options.baseUrl = ''; - } - if (typeof options.isBuild !== 'boolean') { - options.isBuild = false; - } - if (typeof options.paths !== 'object') { - options.paths = {}; - } - if (typeof options.config !== 'object') { - options.config = {}; - } - if (typeof options.catchError === 'undefined') { - options.catchError = false; - } - if (typeof options.recordStats === 'undefined') { - options.recordStats = false; - } - if (typeof options.urlArgs !== 'string') { - options.urlArgs = ''; - } - if (typeof options.onError !== 'function') { - options.onError = defaultOnError; - } - if (!Array.isArray(options.ignoreDuplicateModules)) { - options.ignoreDuplicateModules = []; - } - if (options.baseUrl.length > 0) { - if (!AMDLoader.Utilities.endsWith(options.baseUrl, '/')) { - options.baseUrl += '/'; - } - } - if (typeof options.cspNonce !== 'string') { - options.cspNonce = ''; - } - if (typeof options.preferScriptTags === 'undefined') { - options.preferScriptTags = false; - } - if (options.nodeCachedData && typeof options.nodeCachedData === 'object') { - if (typeof options.nodeCachedData.seed !== 'string') { - options.nodeCachedData.seed = 'seed'; - } - if (typeof options.nodeCachedData.writeDelay !== 'number' || options.nodeCachedData.writeDelay < 0) { - options.nodeCachedData.writeDelay = 1000 * 7; - } - if (!options.nodeCachedData.path || typeof options.nodeCachedData.path !== 'string') { - const err = ensureError(new Error('INVALID cached data configuration, \'path\' MUST be set')); - err.phase = 'configuration'; - options.onError(err); - options.nodeCachedData = undefined; - } - } - return options; - } - static mergeConfigurationOptions(overwrite = null, base = null) { - let result = AMDLoader.Utilities.recursiveClone(base || {}); - // Merge known properties and overwrite the unknown ones - AMDLoader.Utilities.forEachProperty(overwrite, (key, value) => { - if (key === 'ignoreDuplicateModules' && typeof result.ignoreDuplicateModules !== 'undefined') { - result.ignoreDuplicateModules = result.ignoreDuplicateModules.concat(value); - } - else if (key === 'paths' && typeof result.paths !== 'undefined') { - AMDLoader.Utilities.forEachProperty(value, (key2, value2) => result.paths[key2] = value2); - } - else if (key === 'config' && typeof result.config !== 'undefined') { - AMDLoader.Utilities.forEachProperty(value, (key2, value2) => result.config[key2] = value2); - } - else { - result[key] = AMDLoader.Utilities.recursiveClone(value); - } - }); - return ConfigurationOptionsUtil.validateConfigurationOptions(result); - } - } - AMDLoader.ConfigurationOptionsUtil = ConfigurationOptionsUtil; - class Configuration { - constructor(env, options) { - this._env = env; - this.options = ConfigurationOptionsUtil.mergeConfigurationOptions(options); - this._createIgnoreDuplicateModulesMap(); - this._createSortedPathsRules(); - if (this.options.baseUrl === '') { - if (this.options.nodeRequire && this.options.nodeRequire.main && this.options.nodeRequire.main.filename && this._env.isNode) { - let nodeMain = this.options.nodeRequire.main.filename; - let dirnameIndex = Math.max(nodeMain.lastIndexOf('/'), nodeMain.lastIndexOf('\\')); - this.options.baseUrl = nodeMain.substring(0, dirnameIndex + 1); - } - } - } - _createIgnoreDuplicateModulesMap() { - // Build a map out of the ignoreDuplicateModules array - this.ignoreDuplicateModulesMap = {}; - for (let i = 0; i < this.options.ignoreDuplicateModules.length; i++) { - this.ignoreDuplicateModulesMap[this.options.ignoreDuplicateModules[i]] = true; - } - } - _createSortedPathsRules() { - // Create an array our of the paths rules, sorted descending by length to - // result in a more specific -> less specific order - this.sortedPathsRules = []; - AMDLoader.Utilities.forEachProperty(this.options.paths, (from, to) => { - if (!Array.isArray(to)) { - this.sortedPathsRules.push({ - from: from, - to: [to] - }); - } - else { - this.sortedPathsRules.push({ - from: from, - to: to - }); - } - }); - this.sortedPathsRules.sort((a, b) => { - return b.from.length - a.from.length; - }); - } - /** - * Clone current configuration and overwrite options selectively. - * @param options The selective options to overwrite with. - * @result A new configuration - */ - cloneAndMerge(options) { - return new Configuration(this._env, ConfigurationOptionsUtil.mergeConfigurationOptions(options, this.options)); - } - /** - * Get current options bag. Useful for passing it forward to plugins. - */ - getOptionsLiteral() { - return this.options; - } - _applyPaths(moduleId) { - let pathRule; - for (let i = 0, len = this.sortedPathsRules.length; i < len; i++) { - pathRule = this.sortedPathsRules[i]; - if (AMDLoader.Utilities.startsWith(moduleId, pathRule.from)) { - let result = []; - for (let j = 0, lenJ = pathRule.to.length; j < lenJ; j++) { - result.push(pathRule.to[j] + moduleId.substr(pathRule.from.length)); - } - return result; - } - } - return [moduleId]; - } - _addUrlArgsToUrl(url) { - if (AMDLoader.Utilities.containsQueryString(url)) { - return url + '&' + this.options.urlArgs; - } - else { - return url + '?' + this.options.urlArgs; - } - } - _addUrlArgsIfNecessaryToUrl(url) { - if (this.options.urlArgs) { - return this._addUrlArgsToUrl(url); - } - return url; - } - _addUrlArgsIfNecessaryToUrls(urls) { - if (this.options.urlArgs) { - for (let i = 0, len = urls.length; i < len; i++) { - urls[i] = this._addUrlArgsToUrl(urls[i]); - } - } - return urls; - } - /** - * Transform a module id to a location. Appends .js to module ids - */ - moduleIdToPaths(moduleId) { - if (this._env.isNode) { - const isNodeModule = (this.options.amdModulesPattern instanceof RegExp - && !this.options.amdModulesPattern.test(moduleId)); - if (isNodeModule) { - // This is a node module... - if (this.isBuild()) { - // ...and we are at build time, drop it - return ['empty:']; - } - else { - // ...and at runtime we create a `shortcut`-path - return ['node|' + moduleId]; - } - } - } - let result = moduleId; - let results; - if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.isAbsolutePath(result)) { - results = this._applyPaths(result); - for (let i = 0, len = results.length; i < len; i++) { - if (this.isBuild() && results[i] === 'empty:') { - continue; - } - if (!AMDLoader.Utilities.isAbsolutePath(results[i])) { - results[i] = this.options.baseUrl + results[i]; - } - if (!AMDLoader.Utilities.endsWith(results[i], '.js') && !AMDLoader.Utilities.containsQueryString(results[i])) { - results[i] = results[i] + '.js'; - } - } - } - else { - if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.containsQueryString(result)) { - result = result + '.js'; - } - results = [result]; - } - return this._addUrlArgsIfNecessaryToUrls(results); - } - /** - * Transform a module id or url to a location. - */ - requireToUrl(url) { - let result = url; - if (!AMDLoader.Utilities.isAbsolutePath(result)) { - result = this._applyPaths(result)[0]; - if (!AMDLoader.Utilities.isAbsolutePath(result)) { - result = this.options.baseUrl + result; - } - } - return this._addUrlArgsIfNecessaryToUrl(result); - } - /** - * Flag to indicate if current execution is as part of a build. - */ - isBuild() { - return this.options.isBuild; - } - shouldInvokeFactory(strModuleId) { - if (!this.options.isBuild) { - // outside of a build, all factories should be invoked - return true; - } - // during a build, only explicitly marked or anonymous modules get their factories invoked - if (AMDLoader.Utilities.isAnonymousModule(strModuleId)) { - return true; - } - if (this.options.buildForceInvokeFactory && this.options.buildForceInvokeFactory[strModuleId]) { - return true; - } - return false; - } - /** - * Test if module `moduleId` is expected to be defined multiple times - */ - isDuplicateMessageIgnoredFor(moduleId) { - return this.ignoreDuplicateModulesMap.hasOwnProperty(moduleId); - } - /** - * Get the configuration settings for the provided module id - */ - getConfigForModule(moduleId) { - if (this.options.config) { - return this.options.config[moduleId]; - } - } - /** - * Should errors be caught when executing module factories? - */ - shouldCatchError() { - return this.options.catchError; - } - /** - * Should statistics be recorded? - */ - shouldRecordStats() { - return this.options.recordStats; - } - /** - * Forward an error to the error handler. - */ - onError(err) { - this.options.onError(err); - } - } - AMDLoader.Configuration = Configuration; + function ensureError(err) { + if (err instanceof Error) { + return err; + } + const result = new Error(err.message || String(err) || 'Unknown Error'); + if (err.stack) { + result.stack = err.stack; + } + return result; + } + AMDLoader.ensureError = ensureError; + ; + class ConfigurationOptionsUtil { + /** + * Ensure configuration options make sense + */ + static validateConfigurationOptions(options) { + function defaultOnError(err) { + if (err.phase === 'loading') { + console.error('Loading "' + err.moduleId + '" failed'); + console.error(err); + console.error('Here are the modules that depend on it:'); + console.error(err.neededBy); + return; + } + if (err.phase === 'factory') { + console.error('The factory function of "' + err.moduleId + '" has thrown an exception'); + console.error(err); + console.error('Here are the modules that depend on it:'); + console.error(err.neededBy); + return; + } + } + options = options || {}; + if (typeof options.baseUrl !== 'string') { + options.baseUrl = ''; + } + if (typeof options.isBuild !== 'boolean') { + options.isBuild = false; + } + if (typeof options.paths !== 'object') { + options.paths = {}; + } + if (typeof options.config !== 'object') { + options.config = {}; + } + if (typeof options.catchError === 'undefined') { + options.catchError = false; + } + if (typeof options.recordStats === 'undefined') { + options.recordStats = false; + } + if (typeof options.urlArgs !== 'string') { + options.urlArgs = ''; + } + if (typeof options.onError !== 'function') { + options.onError = defaultOnError; + } + if (!Array.isArray(options.ignoreDuplicateModules)) { + options.ignoreDuplicateModules = []; + } + if (options.baseUrl.length > 0) { + if (!AMDLoader.Utilities.endsWith(options.baseUrl, '/')) { + options.baseUrl += '/'; + } + } + if (typeof options.cspNonce !== 'string') { + options.cspNonce = ''; + } + if (typeof options.preferScriptTags === 'undefined') { + options.preferScriptTags = false; + } + if (options.nodeCachedData && typeof options.nodeCachedData === 'object') { + if (typeof options.nodeCachedData.seed !== 'string') { + options.nodeCachedData.seed = 'seed'; + } + if (typeof options.nodeCachedData.writeDelay !== 'number' || options.nodeCachedData.writeDelay < 0) { + options.nodeCachedData.writeDelay = 1000 * 7; + } + if (!options.nodeCachedData.path || typeof options.nodeCachedData.path !== 'string') { + const err = ensureError(new Error('INVALID cached data configuration, \'path\' MUST be set')); + err.phase = 'configuration'; + options.onError(err); + options.nodeCachedData = undefined; + } + } + return options; + } + static mergeConfigurationOptions(overwrite = null, base = null) { + let result = AMDLoader.Utilities.recursiveClone(base || {}); + // Merge known properties and overwrite the unknown ones + AMDLoader.Utilities.forEachProperty(overwrite, (key, value) => { + if (key === 'ignoreDuplicateModules' && typeof result.ignoreDuplicateModules !== 'undefined') { + result.ignoreDuplicateModules = result.ignoreDuplicateModules.concat(value); + } + else if (key === 'paths' && typeof result.paths !== 'undefined') { + AMDLoader.Utilities.forEachProperty(value, (key2, value2) => result.paths[key2] = value2); + } + else if (key === 'config' && typeof result.config !== 'undefined') { + AMDLoader.Utilities.forEachProperty(value, (key2, value2) => result.config[key2] = value2); + } + else { + result[key] = AMDLoader.Utilities.recursiveClone(value); + } + }); + return ConfigurationOptionsUtil.validateConfigurationOptions(result); + } + } + AMDLoader.ConfigurationOptionsUtil = ConfigurationOptionsUtil; + class Configuration { + constructor(env, options) { + this._env = env; + this.options = ConfigurationOptionsUtil.mergeConfigurationOptions(options); + this._createIgnoreDuplicateModulesMap(); + this._createSortedPathsRules(); + if (this.options.baseUrl === '') { + if (this.options.nodeRequire && this.options.nodeRequire.main && this.options.nodeRequire.main.filename && this._env.isNode) { + let nodeMain = this.options.nodeRequire.main.filename; + let dirnameIndex = Math.max(nodeMain.lastIndexOf('/'), nodeMain.lastIndexOf('\\')); + this.options.baseUrl = nodeMain.substring(0, dirnameIndex + 1); + } + } + } + _createIgnoreDuplicateModulesMap() { + // Build a map out of the ignoreDuplicateModules array + this.ignoreDuplicateModulesMap = {}; + for (let i = 0; i < this.options.ignoreDuplicateModules.length; i++) { + this.ignoreDuplicateModulesMap[this.options.ignoreDuplicateModules[i]] = true; + } + } + _createSortedPathsRules() { + // Create an array our of the paths rules, sorted descending by length to + // result in a more specific -> less specific order + this.sortedPathsRules = []; + AMDLoader.Utilities.forEachProperty(this.options.paths, (from, to) => { + if (!Array.isArray(to)) { + this.sortedPathsRules.push({ + from: from, + to: [to] + }); + } + else { + this.sortedPathsRules.push({ + from: from, + to: to + }); + } + }); + this.sortedPathsRules.sort((a, b) => { + return b.from.length - a.from.length; + }); + } + /** + * Clone current configuration and overwrite options selectively. + * @param options The selective options to overwrite with. + * @result A new configuration + */ + cloneAndMerge(options) { + return new Configuration(this._env, ConfigurationOptionsUtil.mergeConfigurationOptions(options, this.options)); + } + /** + * Get current options bag. Useful for passing it forward to plugins. + */ + getOptionsLiteral() { + return this.options; + } + _applyPaths(moduleId) { + let pathRule; + for (let i = 0, len = this.sortedPathsRules.length; i < len; i++) { + pathRule = this.sortedPathsRules[i]; + if (AMDLoader.Utilities.startsWith(moduleId, pathRule.from)) { + let result = []; + for (let j = 0, lenJ = pathRule.to.length; j < lenJ; j++) { + result.push(pathRule.to[j] + moduleId.substr(pathRule.from.length)); + } + return result; + } + } + return [moduleId]; + } + _addUrlArgsToUrl(url) { + if (AMDLoader.Utilities.containsQueryString(url)) { + return url + '&' + this.options.urlArgs; + } + else { + return url + '?' + this.options.urlArgs; + } + } + _addUrlArgsIfNecessaryToUrl(url) { + if (this.options.urlArgs) { + return this._addUrlArgsToUrl(url); + } + return url; + } + _addUrlArgsIfNecessaryToUrls(urls) { + if (this.options.urlArgs) { + for (let i = 0, len = urls.length; i < len; i++) { + urls[i] = this._addUrlArgsToUrl(urls[i]); + } + } + return urls; + } + /** + * Transform a module id to a location. Appends .js to module ids + */ + moduleIdToPaths(moduleId) { + if (this._env.isNode) { + const isNodeModule = (this.options.amdModulesPattern instanceof RegExp + && !this.options.amdModulesPattern.test(moduleId)); + if (isNodeModule) { + // This is a node module... + if (this.isBuild()) { + // ...and we are at build time, drop it + return ['empty:']; + } + else { + // ...and at runtime we create a `shortcut`-path + return ['node|' + moduleId]; + } + } + } + let result = moduleId; + let results; + if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.isAbsolutePath(result)) { + results = this._applyPaths(result); + for (let i = 0, len = results.length; i < len; i++) { + if (this.isBuild() && results[i] === 'empty:') { + continue; + } + if (!AMDLoader.Utilities.isAbsolutePath(results[i])) { + results[i] = this.options.baseUrl + results[i]; + } + if (!AMDLoader.Utilities.endsWith(results[i], '.js') && !AMDLoader.Utilities.containsQueryString(results[i])) { + results[i] = results[i] + '.js'; + } + } + } + else { + if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.containsQueryString(result)) { + result = result + '.js'; + } + results = [result]; + } + return this._addUrlArgsIfNecessaryToUrls(results); + } + /** + * Transform a module id or url to a location. + */ + requireToUrl(url) { + let result = url; + if (!AMDLoader.Utilities.isAbsolutePath(result)) { + result = this._applyPaths(result)[0]; + if (!AMDLoader.Utilities.isAbsolutePath(result)) { + result = this.options.baseUrl + result; + } + } + return this._addUrlArgsIfNecessaryToUrl(result); + } + /** + * Flag to indicate if current execution is as part of a build. + */ + isBuild() { + return this.options.isBuild; + } + shouldInvokeFactory(strModuleId) { + if (!this.options.isBuild) { + // outside of a build, all factories should be invoked + return true; + } + // during a build, only explicitly marked or anonymous modules get their factories invoked + if (AMDLoader.Utilities.isAnonymousModule(strModuleId)) { + return true; + } + if (this.options.buildForceInvokeFactory && this.options.buildForceInvokeFactory[strModuleId]) { + return true; + } + return false; + } + /** + * Test if module `moduleId` is expected to be defined multiple times + */ + isDuplicateMessageIgnoredFor(moduleId) { + return this.ignoreDuplicateModulesMap.hasOwnProperty(moduleId); + } + /** + * Get the configuration settings for the provided module id + */ + getConfigForModule(moduleId) { + if (this.options.config) { + return this.options.config[moduleId]; + } + } + /** + * Should errors be caught when executing module factories? + */ + shouldCatchError() { + return this.options.catchError; + } + /** + * Should statistics be recorded? + */ + shouldRecordStats() { + return this.options.recordStats; + } + /** + * Forward an error to the error handler. + */ + onError(err) { + this.options.onError(err); + } + } + AMDLoader.Configuration = Configuration; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -539,503 +539,503 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - /** - * Load `scriptSrc` only once (avoid multiple