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-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 2f52208fa18..e6011aeed86 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -120,6 +120,7 @@ ], "rules": { "local/code-no-test-only": "error", + "local/code-no-test-async-suite": "warn", "local/code-no-unexternalized-strings": "off" } }, @@ -239,6 +240,7 @@ "console", "cookie", "crypto", + "dns", "electron", "events", "fs", 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/workflows/basic.yml b/.github/workflows/basic.yml index 210b8058982..012c4247abe 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey @@ -83,7 +83,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey @@ -145,7 +145,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dac5f9e073b..cd32a2b50b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - uses: actions/setup-python@v4 with: @@ -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 @@ -115,7 +115,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey @@ -186,7 +186,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey @@ -259,7 +259,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey diff --git a/.github/workflows/monaco-editor.yml b/.github/workflows/monaco-editor.yml index a86f94bc331..a276604164e 100644 --- a/.github/workflows/monaco-editor.yml +++ b/.github/workflows/monaco-editor.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Compute node modules cache key id: nodeModulesCacheKey @@ -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/rich-navigation.yml.off b/.github/workflows/rich-navigation.yml.off index c36c83dcb14..a05130cbce4 100644 --- a/.github/workflows/rich-navigation.yml.off +++ b/.github/workflows/rich-navigation.yml.off @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - name: Install dependencies if: steps.caching-stage.outputs.cache-hit != 'true' diff --git a/.nvmrc b/.nvmrc index 5cb297e3e48..0f9eb824458 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.17 +18.15 \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index baa3cfb0c8b..3bea8e7c076 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -244,11 +244,11 @@ "--disable-features=CalculateNativeWinOcclusion", "--disable-extension=vscode.vscode-api-tests" ], + "userDataDir": "${userHome}/.vscode-oss-dev", "webRoot": "${workspaceFolder}", "cascadeTerminateToConfigurations": [ "Attach to Extension Host" ], - "userDataDir": "${workspaceFolder}/.profile-oss", "pauseForSourceMap": false, "outFiles": [ "${workspaceFolder}/out/**/*.js" diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 1d39b63dd9f..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:\"July 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 60c8c5a6aec..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:\"July 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 92fddeef220..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:\"July 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 9f66c4771f6..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:\"July 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: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-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 -label:inline-chat" + "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 43ff3912c72..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:\"July 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 65cfed180a5..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:\"July 2023\"" + "value": "$milestone=milestone:\"August 2023\"" }, { "kind": 1, diff --git a/.vscode/settings.json b/.vscode/settings.json index 5d2930c5a92..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, diff --git a/.yarnrc b/.yarnrc index 3af2059dbd7..6826ca67118 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "22.3.14" -ms_build_id "21893604" +target "25.4.0" +ms_build_id "22958381" runtime "electron" build_from_source "true" diff --git a/CodeQL.yml b/CodeQL.yml index 9ef07560af3..eecb813a96f 100644 --- a/CodeQL.yml +++ b/CodeQL.yml @@ -21,3 +21,9 @@ path_classifiers: - "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 8aa191023da..3fb6234cbcf 100644 --- a/build/azure-pipelines/alpine/cli-build-alpine.yml +++ b/build/azure-pipelines/alpine/cli-build-alpine.yml @@ -11,11 +11,9 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - template: ../distro/download-distro.yml - # Install yarn as the ARM64 build agent is using vanilla Ubuntu - ${{ if eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true) }}: - task: Npm@1 @@ -30,8 +28,7 @@ steps: workingDirectory: build displayName: Install pipeline build - - script: node .build/distro/cli-patches/index.js - displayName: Apply distro patches + - template: ../cli/cli-apply-patches.yml - task: Npm@1 displayName: Download openssl prebuilt @@ -56,13 +53,6 @@ steps: sudo ln -s "/usr/bin/g++" "/usr/bin/musl-g++" || echo "link exists" displayName: Install musl build dependencies - - script: node build/azure-pipelines/cli/prepare.js - displayName: Prepare CLI build - env: - VSCODE_CLI_PREPARE_ROOT: $(Build.SourcesDirectory)/.build/distro - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - GITHUB_TOKEN: "$(github-distro-mixin-password)" - - template: ../cli/install-rust-posix.yml parameters: targets: @@ -76,6 +66,7 @@ steps: parameters: VSCODE_CLI_TARGET: aarch64-unknown-linux-musl VSCODE_CLI_ARTIFACT: vscode_cli_alpine_arm64_cli + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_ENV: CXX_aarch64-unknown-linux-musl: musl-g++ CC_aarch64-unknown-linux-musl: musl-gcc @@ -88,6 +79,7 @@ steps: parameters: VSCODE_CLI_TARGET: x86_64-unknown-linux-musl VSCODE_CLI_ARTIFACT: vscode_cli_alpine_x64_cli + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_ENV: CXX_aarch64-unknown-linux-musl: musl-g++ CC_aarch64-unknown-linux-musl: musl-gcc diff --git a/build/azure-pipelines/alpine/product-build-alpine.yml b/build/azure-pipelines/alpine/product-build-alpine.yml index eed3623addc..20923b65b60 100644 --- a/build/azure-pipelines/alpine/product-build-alpine.yml +++ b/build/azure-pipelines/alpine/product-build-alpine.yml @@ -1,11 +1,7 @@ steps: - task: NodeTool@0 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')) + versionSpec: "18.x" - template: ../distro/download-distro.yml @@ -46,7 +42,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 @@ -67,6 +66,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 +81,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')) diff --git a/build/azure-pipelines/cli/cli-apply-patches.yml b/build/azure-pipelines/cli/cli-apply-patches.yml index 35b429375c0..b96aa4ef7dd 100644 --- a/build/azure-pipelines/cli/cli-apply-patches.yml +++ b/build/azure-pipelines/cli/cli-apply-patches.yml @@ -1,5 +1,8 @@ steps: - template: ../distro/download-distro.yml + - script: node build/azure-pipelines/distro/mixin-quality + displayName: Mixin distro quality + - script: node .build/distro/cli-patches/index.js displayName: Apply distro patches diff --git a/build/azure-pipelines/cli/cli-compile-and-publish.yml b/build/azure-pipelines/cli/cli-compile-and-publish.yml index a6468ad3ae9..e4ef4af5481 100644 --- a/build/azure-pipelines/cli/cli-compile-and-publish.yml +++ b/build/azure-pipelines/cli/cli-compile-and-publish.yml @@ -1,4 +1,6 @@ parameters: + - name: VSCODE_QUALITY + type: string - name: VSCODE_CLI_TARGET type: string - name: VSCODE_CLI_ARTIFACT @@ -11,6 +13,13 @@ parameters: default: false steps: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - pwsh: Write-Host "##vso[task.setvariable variable=VSCODE_CLI_PRODUCT_JSON]$(Build.SourcesDirectory)/product.json" + displayName: Set product.json path + - ${{ else }}: + - pwsh: Write-Host "##vso[task.setvariable variable=VSCODE_CLI_PRODUCT_JSON]$(Build.SourcesDirectory)/.build/distro/mixin/${{ parameters.VSCODE_QUALITY }}/product.json" + displayName: Set product.json path + - ${{ if parameters.VSCODE_CHECK_ONLY }}: - script: rustup component add clippy && cargo clippy --target ${{ parameters.VSCODE_CLI_TARGET }} --bin=code displayName: Lint ${{ parameters.VSCODE_CLI_TARGET }} @@ -26,6 +35,7 @@ steps: workingDirectory: $(Build.SourcesDirectory)/cli env: CARGO_NET_GIT_FETCH_WITH_CLI: true + VSCODE_CLI_COMMIT: $(Build.SourceVersion) ${{ each pair in parameters.VSCODE_CLI_ENV }}: ${{ pair.key }}: ${{ pair.value }} @@ -33,6 +43,11 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" + $AppProductJson = Get-Content -Raw -Path "$env:VSCODE_CLI_PRODUCT_JSON" | ConvertFrom-Json + $env:VSCODE_CLI_APPLICATION_NAME = $AppProductJson.applicationName + + Write-Host "##vso[task.setvariable variable=VSCODE_CLI_APPLICATION_NAME]$env:VSCODE_CLI_APPLICATION_NAME" + Move-Item -Path $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code.exe -Destination "$(Build.ArtifactStagingDirectory)/${env:VSCODE_CLI_APPLICATION_NAME}.exe" - task: ArchiveFiles@2 @@ -49,7 +64,10 @@ steps: - ${{ else }}: - script: | set -e - mv $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code $(Build.ArtifactStagingDirectory)/$(VSCODE_CLI_APPLICATION_NAME) + VSCODE_CLI_APPLICATION_NAME=$(node -p "require(\"$VSCODE_CLI_PRODUCT_JSON\").applicationName") + echo "##vso[task.setvariable variable=VSCODE_CLI_APPLICATION_NAME]$VSCODE_CLI_APPLICATION_NAME" + + mv $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code $(Build.ArtifactStagingDirectory)/$VSCODE_CLI_APPLICATION_NAME - ${{ if contains(parameters.VSCODE_CLI_TARGET, '-darwin') }}: - task: ArchiveFiles@2 diff --git a/build/azure-pipelines/cli/install-rust-posix.yml b/build/azure-pipelines/cli/install-rust-posix.yml index ee1e26f3505..d6022a44f7a 100644 --- a/build/azure-pipelines/cli/install-rust-posix.yml +++ b/build/azure-pipelines/cli/install-rust-posix.yml @@ -1,7 +1,7 @@ parameters: - name: channel type: string - default: 1.65.0 + default: 1.71.0 - name: targets default: [] type: object diff --git a/build/azure-pipelines/cli/install-rust-win32.yml b/build/azure-pipelines/cli/install-rust-win32.yml index 62ff0ce4ec5..373c41dd95f 100644 --- a/build/azure-pipelines/cli/install-rust-win32.yml +++ b/build/azure-pipelines/cli/install-rust-win32.yml @@ -1,7 +1,7 @@ parameters: - name: channel type: string - default: 1.65.0 + default: 1.71.0 - name: targets default: [] type: object diff --git a/build/azure-pipelines/cli/prepare.js b/build/azure-pipelines/cli/prepare.js deleted file mode 100644 index bd11bc6a0a4..00000000000 --- a/build/azure-pipelines/cli/prepare.js +++ /dev/null @@ -1,93 +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 getVersion_1 = require("../../lib/getVersion"); -const fs = require("fs"); -const path = require("path"); -const packageJson = require("../../../package.json"); -const root = process.env.VSCODE_CLI_PREPARE_ROOT || path.dirname(path.dirname(path.dirname(__dirname))); -const readJSON = (path) => JSON.parse(fs.readFileSync(path, 'utf8')); -let productJsonPath; -const isOSS = process.env.VSCODE_QUALITY === 'oss' || !process.env.VSCODE_QUALITY; -if (isOSS) { - productJsonPath = path.join(root, 'product.json'); -} -else { - productJsonPath = path.join(root, 'mixin', process.env.VSCODE_QUALITY, 'product.json'); -} -console.error('Loading product.json from', productJsonPath); -const product = readJSON(productJsonPath); -const allProductsAndQualities = isOSS ? [product] : fs.readdirSync(path.join(root, 'mixin')) - .map(quality => ({ quality, json: readJSON(path.join(root, 'mixin', quality, 'product.json')) })); -const commit = (0, getVersion_1.getVersion)(root); -const makeQualityMap = (m) => { - const output = {}; - for (const { quality, json } of allProductsAndQualities) { - output[quality] = m(json, quality); - } - return output; -}; -/** - * Sets build environment variables for the CLI for current contextual info. - */ -const setLauncherEnvironmentVars = () => { - const vars = new Map([ - ['VSCODE_CLI_ALREADY_PREPARED', 'true'], - ['VSCODE_CLI_REMOTE_LICENSE_TEXT', product.serverLicense?.join('\\n')], - ['VSCODE_CLI_REMOTE_LICENSE_PROMPT', product.serverLicensePrompt], - ['VSCODE_CLI_AI_KEY', product.aiConfig?.cliKey], - ['VSCODE_CLI_AI_ENDPOINT', product.aiConfig?.cliEndpoint], - ['VSCODE_CLI_VERSION', packageJson.version], - ['VSCODE_CLI_UPDATE_ENDPOINT', product.updateUrl], - ['VSCODE_CLI_QUALITY', product.quality], - ['VSCODE_CLI_NAME_SHORT', product.nameShort], - ['VSCODE_CLI_NAME_LONG', product.nameLong], - ['VSCODE_CLI_QUALITYLESS_PRODUCT_NAME', product.nameLong.replace(/ - [a-z]+$/i, '')], - ['VSCODE_CLI_DOCUMENTATION_URL', product.documentationUrl], - ['VSCODE_CLI_APPLICATION_NAME', product.applicationName], - ['VSCODE_CLI_EDITOR_WEB_URL', product.tunnelApplicationConfig?.editorWebUrl], - ['VSCODE_CLI_TUNNEL_SERVICE_MUTEX', product.win32TunnelServiceMutex], - ['VSCODE_CLI_TUNNEL_CLI_MUTEX', product.win32TunnelMutex], - ['VSCODE_CLI_COMMIT', commit], - ['VSCODE_CLI_DEFAULT_PARENT_DATA_DIR', product.dataFolderName], - [ - 'VSCODE_CLI_WIN32_APP_IDS', - !isOSS && JSON.stringify(makeQualityMap(json => Object.entries(json) - .filter(([key]) => /^win32.*AppId$/.test(key)) - .map(([, value]) => String(value).replace(/[{}]/g, '')))), - ], - [ - 'VSCODE_CLI_NAME_LONG_MAP', - !isOSS && JSON.stringify(makeQualityMap(json => json.nameLong)), - ], - [ - 'VSCODE_CLI_APPLICATION_NAME_MAP', - !isOSS && JSON.stringify(makeQualityMap(json => json.applicationName)), - ], - [ - 'VSCODE_CLI_SERVER_NAME_MAP', - !isOSS && JSON.stringify(makeQualityMap(json => json.serverApplicationName)), - ], - [ - 'VSCODE_CLI_QUALITY_DOWNLOAD_URIS', - !isOSS && JSON.stringify(makeQualityMap(json => json.downloadUrl)), - ], - ]); - if (process.env.VSCODE_CLI_PREPARE_OUTPUT === 'json') { - console.log(JSON.stringify([...vars].filter(([, v]) => !!v))); - } - else { - for (const [key, value] of vars) { - if (value) { - console.log(`##vso[task.setvariable variable=${key}]${value}`); - } - } - } -}; -if (require.main === module) { - setLauncherEnvironmentVars(); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlcGFyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInByZXBhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxxREFBa0Q7QUFDbEQseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3QixxREFBcUQ7QUFFckQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDeEcsTUFBTSxRQUFRLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUU3RSxJQUFJLGVBQXVCLENBQUM7QUFDNUIsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEtBQUssS0FBSyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7QUFDbEYsSUFBSSxLQUFLLEVBQUU7SUFDVixlQUFlLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsY0FBYyxDQUFDLENBQUM7Q0FDbEQ7S0FBTTtJQUNOLGVBQWUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFlLEVBQUUsY0FBYyxDQUFDLENBQUM7Q0FDeEY7QUFFRCxPQUFPLENBQUMsS0FBSyxDQUFDLDJCQUEyQixFQUFFLGVBQWUsQ0FBQyxDQUFDO0FBQzVELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQztBQUMxQyxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFD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 \ No newline at end of file diff --git a/build/azure-pipelines/cli/prepare.ts b/build/azure-pipelines/cli/prepare.ts deleted file mode 100644 index 7157dae3ecd..00000000000 --- a/build/azure-pipelines/cli/prepare.ts +++ /dev/null @@ -1,99 +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 { getVersion } from '../../lib/getVersion'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as packageJson from '../../../package.json'; - -const root = process.env.VSCODE_CLI_PREPARE_ROOT || path.dirname(path.dirname(path.dirname(__dirname))); -const readJSON = (path: string) => JSON.parse(fs.readFileSync(path, 'utf8')); - -let productJsonPath: string; -const isOSS = process.env.VSCODE_QUALITY === 'oss' || !process.env.VSCODE_QUALITY; -if (isOSS) { - productJsonPath = path.join(root, 'product.json'); -} else { - productJsonPath = path.join(root, 'mixin', process.env.VSCODE_QUALITY!, 'product.json'); -} - -console.error('Loading product.json from', productJsonPath); -const product = readJSON(productJsonPath); -const allProductsAndQualities = isOSS ? [product] : fs.readdirSync(path.join(root, 'mixin')) - .map(quality => ({ quality, json: readJSON(path.join(root, 'mixin', quality, 'product.json')) })); -const commit = getVersion(root); - -const makeQualityMap = (m: (productJson: any, quality: string) => T): Record => { - const output: Record = {}; - for (const { quality, json } of allProductsAndQualities) { - output[quality] = m(json, quality); - } - return output; -}; - -/** - * Sets build environment variables for the CLI for current contextual info. - */ -const setLauncherEnvironmentVars = () => { - const vars = new Map([ - ['VSCODE_CLI_ALREADY_PREPARED', 'true'], - ['VSCODE_CLI_REMOTE_LICENSE_TEXT', product.serverLicense?.join('\\n')], - ['VSCODE_CLI_REMOTE_LICENSE_PROMPT', product.serverLicensePrompt], - ['VSCODE_CLI_AI_KEY', product.aiConfig?.cliKey], - ['VSCODE_CLI_AI_ENDPOINT', product.aiConfig?.cliEndpoint], - ['VSCODE_CLI_VERSION', packageJson.version], - ['VSCODE_CLI_UPDATE_ENDPOINT', product.updateUrl], - ['VSCODE_CLI_QUALITY', product.quality], - ['VSCODE_CLI_NAME_SHORT', product.nameShort], - ['VSCODE_CLI_NAME_LONG', product.nameLong], - ['VSCODE_CLI_QUALITYLESS_PRODUCT_NAME', product.nameLong.replace(/ - [a-z]+$/i, '')], - ['VSCODE_CLI_DOCUMENTATION_URL', product.documentationUrl], - ['VSCODE_CLI_APPLICATION_NAME', product.applicationName], - ['VSCODE_CLI_EDITOR_WEB_URL', product.tunnelApplicationConfig?.editorWebUrl], - ['VSCODE_CLI_TUNNEL_SERVICE_MUTEX', product.win32TunnelServiceMutex], - ['VSCODE_CLI_TUNNEL_CLI_MUTEX', product.win32TunnelMutex], - ['VSCODE_CLI_COMMIT', commit], - ['VSCODE_CLI_DEFAULT_PARENT_DATA_DIR', product.dataFolderName], - [ - 'VSCODE_CLI_WIN32_APP_IDS', - !isOSS && JSON.stringify( - makeQualityMap(json => Object.entries(json) - .filter(([key]) => /^win32.*AppId$/.test(key)) - .map(([, value]) => String(value).replace(/[{}]/g, ''))), - ), - ], - [ - 'VSCODE_CLI_NAME_LONG_MAP', - !isOSS && JSON.stringify(makeQualityMap(json => json.nameLong)), - ], - [ - 'VSCODE_CLI_APPLICATION_NAME_MAP', - !isOSS && JSON.stringify(makeQualityMap(json => json.applicationName)), - ], - [ - 'VSCODE_CLI_SERVER_NAME_MAP', - !isOSS && JSON.stringify(makeQualityMap(json => json.serverApplicationName)), - ], - [ - 'VSCODE_CLI_QUALITY_DOWNLOAD_URIS', - !isOSS && JSON.stringify(makeQualityMap(json => json.downloadUrl)), - ], - ]); - - if (process.env.VSCODE_CLI_PREPARE_OUTPUT === 'json') { - console.log(JSON.stringify([...vars].filter(([, v]) => !!v))); - } else { - for (const [key, value] of vars) { - if (value) { - console.log(`##vso[task.setvariable variable=${key}]${value}`); - } - } - } - -}; - -if (require.main === module) { - setLauncherEnvironmentVars(); -} 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlQXNzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGVBc3NldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLHlCQUF5QjtBQUV6QixpQ0FBaUM7QUFDakMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG1DQUFnQztBQWFoQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtJQUM5QixPQUFPLENBQUMsS0FBSyxDQUFDLDJEQUEyRCxDQUFDLENBQUM7SUFDM0UsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0NBQ2pCO0FBRUQsd0ZBQXdGO0FBQ3hGLFNBQVMsV0FBVyxDQUFDLE9BQWUsRUFBRSxFQUFVLEVBQUUsSUFBWSxFQUFFLElBQVk7SUFDM0UsUUFBUSxFQUFFLEVBQUU7UUFDWCxLQUFLLE9BQU87WUFDWCxRQUFRLE9BQU8sRUFBRTtnQkFDaEIsS0FBSyxRQUFRLENBQUMsQ0FBQztvQkFDZCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUM7b0JBQzFELFFBQVEsSUFBSSxFQUFFO3dCQUNiLEtBQUssU0FBUzs0QkFDYixPQUFPLEdBQUcsS0FBSyxVQUFVLENBQUM7d0JBQzNCLEtBQUssT0FBTzs0QkFDWCxPQUFPLEtBQUssQ0FBQzt3QkFDZCxLQUFLLFlBQVk7NEJBQ2hCLE9BQU8sR0FBRyxLQUFLLE9BQU8sQ0FBQzt3QkFDeEI7NEJBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbkU7aUJBQ0Q7Z0JBQ0QsS0FBSyxRQUFRO29CQUNaLElBQUksSUFBSSxLQUFLLE9BQU8s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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlQXNzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGVBc3NldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLHlCQUF5QjtBQUV6QixpQ0FBaUM7QUFDakMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG1DQUFnQztBQWFoQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtJQUM5QixPQUFPLENBQUMsS0FBSyxDQUFDLDJEQUEyRCxDQUFDLENBQUM7SUFDM0UsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0NBQ2pCO0FBRUQsd0ZBQXdGO0FBQ3hGLFNBQVMsV0FBVyxDQUFDLE9BQWUsRUFBRSxFQUFVLEVBQUUsSUFBWSxFQUFFLElBQVk7SUFDM0UsUUFBUSxFQUFFLEVBQUU7UUFDWCxLQUFLLE9BQU87WUFDWCxRQUFRLE9BQU8sRUFBRTtnQkFDaEIsS0FBSyxRQUFRLENBQUMsQ0FBQztvQkFDZCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUM7b0JBQzFELFFBQVEsSUFBSSxFQUFFO3dCQUNiLEtBQUssU0FBUzs0QkFDYixPQUFPLEdBQUcsS0FBSyxVQUFVLENBQUM7d0JBQzNCLEtBQUssT0FBTzs0QkFDWCxPQUFPLEtBQUssQ0FBQzt3QkFDZCxLQUFLLFlBQVk7NEJBQ2hCLE9BQU8sR0FBRyxLQUFLLE9BQU8sQ0FBQzt3QkFDeEI7NEJBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztxQkFDbkU7aUJBQ0Q7Z0JBQ0QsS0FBSyxRQUFRO29CQUNaLElBQUksSUFBSSxLQUFLLE9BQU8s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 \ 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/cli-build-darwin.yml b/build/azure-pipelines/darwin/cli-build-darwin.yml index 8d98f0e93aa..ae8f0e84652 100644 --- a/build/azure-pipelines/darwin/cli-build-darwin.yml +++ b/build/azure-pipelines/darwin/cli-build-darwin.yml @@ -14,7 +14,7 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../cli/cli-apply-patches.yml @@ -34,13 +34,6 @@ steps: tar -xvzf $(Build.ArtifactStagingDirectory)/vscode-internal-openssl-prebuilt-0.0.8.tgz --strip-components=1 --directory=$(Build.ArtifactStagingDirectory)/openssl displayName: Extract openssl prebuilt - - script: node build/azure-pipelines/cli/prepare.js - displayName: Prepare CLI build - env: - VSCODE_CLI_PREPARE_ROOT: $(Build.SourcesDirectory)/.build/distro - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - GITHUB_TOKEN: "$(github-distro-mixin-password)" - - template: ../cli/install-rust-posix.yml parameters: targets: @@ -52,6 +45,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: x86_64-apple-darwin VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_darwin_x64_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} @@ -62,6 +56,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: aarch64-apple-darwin VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_darwin_arm64_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} diff --git a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml index ec9ac07e606..0ee0d9be4a6 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml @@ -7,7 +7,7 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - script: node build/setup-npm-registry.js $NPM_REGISTRY build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) @@ -16,7 +16,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" workingDirectory: build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index 076788581c0..3cbe9d329fb 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - task: UseDotNet@2 inputs: diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 955b19749a6..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') }}: diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index c52e8129a55..3e4a2a8d8a2 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - template: ../distro/download-distro.yml @@ -19,7 +19,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" workingDirectory: build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 054a3caec37..dedc227caa7 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -18,7 +18,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../distro/download-distro.yml @@ -60,7 +60,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 @@ -212,16 +215,16 @@ 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: $(ARTIFACT_PREFIX)sbom_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: $(ARTIFACT_PREFIX)sbom_server_darwin_$(VSCODE_ARCH)_sbom diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index fe05f56723b..6aabd52c76c 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -9,5 +9,5 @@ pr: none steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - template: ./distro/download-distro.yml diff --git a/build/azure-pipelines/linux/cli-build-linux.yml b/build/azure-pipelines/linux/cli-build-linux.yml index 4df126682cf..b77b78aa1a8 100644 --- a/build/azure-pipelines/linux/cli-build-linux.yml +++ b/build/azure-pipelines/linux/cli-build-linux.yml @@ -17,7 +17,7 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../cli/cli-apply-patches.yml @@ -45,13 +45,6 @@ steps: - bash: sudo apt-get install -yq gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu displayName: Install arm64 toolchains - - script: node build/azure-pipelines/cli/prepare.js - displayName: Prepare CLI build - env: - VSCODE_CLI_PREPARE_ROOT: $(Build.SourcesDirectory)/.build/distro - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - GITHUB_TOKEN: "$(github-distro-mixin-password)" - - template: ../cli/install-rust-posix.yml parameters: targets: @@ -65,6 +58,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: aarch64-unknown-linux-gnu VSCODE_CLI_ARTIFACT: vscode_cli_linux_arm64_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} @@ -76,6 +70,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: x86_64-unknown-linux-gnu VSCODE_CLI_ARTIFACT: vscode_cli_linux_x64_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} @@ -86,6 +81,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: armv7-unknown-linux-gnueabihf VSCODE_CLI_ARTIFACT: vscode_cli_linux_armhf_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} diff --git a/build/azure-pipelines/linux/install.sh b/build/azure-pipelines/linux/install.sh new file mode 100755 index 00000000000..c8d74687080 --- /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/114.0.5735.199/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/114.0.5735.199:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/114.0.5735.199:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/114.0.5735.199: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/product-build-linux-test.yml b/build/azure-pipelines/linux/product-build-linux-test.yml index 7532c43c7c9..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: | diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 87f4cd800da..2145e588b1c 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -20,7 +20,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../distro/download-distro.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" @@ -105,69 +110,72 @@ steps: 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-$(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 \ + -e VSCODE_SKIP_NODE_VERSION_CHECK \ + -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 + VSCODE_SKIP_NODE_VERSION_CHECK: 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 }}: + # Ref https://github.com/microsoft/vscode/issues/189019 + # for the node-gyp rebuild step + - 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 + + cd node_modules/native-keymap && npx node-gyp@9.4.0 -y rebuild --debug + cd ../.. && ./.github/workflows/check-clean-git-state.sh + 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 @@ -188,12 +196,34 @@ steps: 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) - tar -czf $ARCHIVE_PATH -C .. VSCode-linux-$(VSCODE_ARCH) echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client + - ${{ 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 @@ -233,20 +263,15 @@ 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: $(ARTIFACT_PREFIX)vscode_cli_linux_$(VSCODE_ARCH)_cli - patterns: "**" - path: $(Build.ArtifactStagingDirectory)/cli - displayName: Download VS Code CLI - - - script: | + - 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 + 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 @@ -254,6 +279,16 @@ steps: 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" @@ -290,16 +325,16 @@ 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: $(ARTIFACT_PREFIX)sbom_vscode_client_linux_$(VSCODE_ARCH) - - 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: $(ARTIFACT_PREFIX)sbom_vscode_server_linux_$(VSCODE_ARCH) diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index c028cc58e04..9146318cc12 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - task: DownloadPipelineArtifact@0 displayName: "Download Pipeline Artifact" 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..2884ba79cd7 100644 --- a/build/azure-pipelines/oss/product-build-pr-cache-linux.yml +++ b/build/azure-pipelines/oss/product-build-pr-cache-linux.yml @@ -5,7 +5,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - script: node build/setup-npm-registry.js $NPM_REGISTRY condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) @@ -28,7 +28,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 @@ -39,6 +42,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/oss/product-build-pr-cache-win32.yml b/build/azure-pipelines/oss/product-build-pr-cache-win32.yml index 61b0bf37d25..2901d0f0f5e 100644 --- a/build/azure-pipelines/oss/product-build-pr-cache-win32.yml +++ b/build/azure-pipelines/oss/product-build-pr-cache-win32.yml @@ -5,7 +5,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - powershell: node build/setup-npm-registry.js $env:NPM_REGISTRY condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) @@ -31,7 +31,10 @@ steps: . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { npm config set registry "$env:NPM_REGISTRY" --location=project } - exec { 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. + exec { Add-Content -Path .npmrc -Value "always-auth=true" } exec { yarn config set registry "$env:NPM_REGISTRY" } condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM & Yarn diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 1be2db3b5fa..2d4ad8aa84a 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -143,18 +143,6 @@ 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 @@ -382,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 @@ -398,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 @@ -414,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 @@ -431,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 @@ -458,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 @@ -474,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 @@ -499,6 +481,7 @@ stages: - job: LinuxAlpine variables: VSCODE_ARCH: x64 + NPM_ARCH: x64 steps: - template: alpine/product-build-alpine.yml @@ -507,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..4bcf3fd9114 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -5,7 +5,7 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ./distro/download-distro.yml @@ -38,7 +38,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 @@ -49,7 +52,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.yml b/build/azure-pipelines/product-publish.yml index 2e2d735da05..db4424735e9 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" diff --git a/build/azure-pipelines/product-release.yml b/build/azure-pipelines/product-release.yml index 8be900a9b41..498d7c58607 100644 --- a/build/azure-pipelines/product-release.yml +++ b/build/azure-pipelines/product-release.yml @@ -5,7 +5,7 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - task: AzureCLI@2 inputs: diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 031fafd4c75..db0d5ce1894 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -12,7 +12,7 @@ pool: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - bash: | TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index 8c81ee20401..16304177c7c 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -52,7 +52,7 @@ stages: outputFormat: "pre" - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - template: ./distro/download-distro.yml @@ -67,7 +67,10 @@ stages: . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { npm config set registry "$env:NPM_REGISTRY" --location=project } - exec { 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. + exec { Add-Content -Path .npmrc -Value "always-auth=true" } exec { yarn config set registry "$env:NPM_REGISTRY" } condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM & Yarn @@ -176,7 +179,7 @@ stages: toolMajorVersion: "V2" - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - template: ./distro/download-distro.yml @@ -190,7 +193,10 @@ stages: - 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 diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index a941ae9adfe..afaf43c55c2 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - template: ../distro/download-distro.yml @@ -42,7 +42,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 @@ -53,6 +56,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/win32/cli-build-win32.yml b/build/azure-pipelines/win32/cli-build-win32.yml index 9a155a0c0be..6eb5ac41f5c 100644 --- a/build/azure-pipelines/win32/cli-build-win32.yml +++ b/build/azure-pipelines/win32/cli-build-win32.yml @@ -17,7 +17,7 @@ parameters: steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../cli/cli-apply-patches.yml @@ -36,13 +36,6 @@ steps: tar -xvzf $(Build.ArtifactStagingDirectory)/vscode-internal-openssl-prebuilt-0.0.8.tgz --strip-components=1 --directory=$(Build.ArtifactStagingDirectory)/openssl displayName: Extract openssl prebuilt - - powershell: node build/azure-pipelines/cli/prepare.js - displayName: Prepare CLI build - env: - VSCODE_CLI_PREPARE_ROOT: $(Build.SourcesDirectory)/.build/distro - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - GITHUB_TOKEN: "$(github-distro-mixin-password)" - - template: ../cli/install-rust-win32.yml parameters: targets: @@ -56,6 +49,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: x86_64-pc-windows-msvc VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_win32_x64_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} @@ -67,6 +61,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: aarch64-pc-windows-msvc VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_win32_arm64_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} @@ -78,6 +73,7 @@ steps: - ${{ if eq(parameters.VSCODE_BUILD_WIN32_32BIT, true) }}: - template: ../cli/cli-compile-and-publish.yml parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} VSCODE_CLI_TARGET: i686-pc-windows-msvc VSCODE_CLI_ARTIFACT: unsigned_vscode_cli_win32_ia32_cli VSCODE_CHECK_ONLY: ${{ parameters.VSCODE_CHECK_ONLY }} diff --git a/build/azure-pipelines/win32/product-build-win32-cli-sign.yml b/build/azure-pipelines/win32/product-build-win32-cli-sign.yml index 31bffda4788..61b71973280 100644 --- a/build/azure-pipelines/win32/product-build-win32-cli-sign.yml +++ b/build/azure-pipelines/win32/product-build-win32-cli-sign.yml @@ -10,7 +10,7 @@ steps: - task: NodeTool@0 displayName: "Use Node.js" inputs: - versionSpec: "16.x" + versionSpec: "18.x" - powershell: node build/setup-npm-registry.js $env:NPM_REGISTRY build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) @@ -20,7 +20,10 @@ steps: . azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { npm config set registry "$env:NPM_REGISTRY" --location=project } - exec { 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. + exec { Add-Content -Path .npmrc -Value "always-auth=true" } exec { yarn config set registry "$env:NPM_REGISTRY" } workingDirectory: build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index 630e226a742..3a24e95657a 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -15,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') }}: diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 9cadfab45a0..8b61ee52015 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -20,7 +20,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "16.x" + versionSpec: "18.x" - task: UsePythonVersion@0 inputs: @@ -73,7 +73,10 @@ steps: . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { npm config set registry "$env:NPM_REGISTRY" --location=project } - exec { 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. + exec { Add-Content -Path .npmrc -Value "always-auth=true" } exec { yarn config set registry "$env:NPM_REGISTRY" } condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM & Yarn @@ -94,9 +97,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')) @@ -136,7 +142,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 @@ -148,7 +154,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 @@ -263,7 +269,7 @@ steps: $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 } + 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 @@ -273,7 +279,7 @@ steps: $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 } + 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) @@ -312,10 +318,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: $(ARTIFACT_PREFIX)sbom_client_win32_$(VSCODE_ARCH) - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: @@ -323,6 +325,10 @@ 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: $(ARTIFACT_PREFIX)sbom_server_win32_$(VSCODE_ARCH) diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index beb2b9730b0..c0d1e51c571 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 +fc51b54078335a8d687e0dda5e65e0b9916d9678d13d8f84b1dc91bceaac6b94 *electron-v25.4.0-darwin-arm64-symbols.zip +8a1f0cc5c8fea926360347f9448a766f56fe4fea8418ce11ca5755be933dcae2 *electron-v25.4.0-darwin-arm64.zip +a221e0fb48d178940e77bb96c8bfe26fb5f4eb5b916c93d7b771d0f216e9f310 *electron-v25.4.0-darwin-x64-symbols.zip +f6e65c0d27e99121e53eb52335ef42653108bfa5cb8de06c35853c253fafc000 *electron-v25.4.0-darwin-x64.zip +e1006e2a327f23fe73c172789a9957c83e1b7563358908c0d1afb6f2a95f305c *electron-v25.4.0-linux-arm64-symbols.zip +2225b64fbe7ff602160ad371ede9a4eb79534d865dbac3a6fcc252faf0214240 *electron-v25.4.0-linux-arm64.zip +7ff71baa0e0c55728569a3a05fb1aadd079e2dd79bbc7bb4ce460c8fefe1c422 *electron-v25.4.0-linux-armv7l-symbols.zip +71469e85d3b3bbf40b698ffb3e86b96eef854c2bc63096900d1c1a606f8b9bb4 *electron-v25.4.0-linux-armv7l.zip +28a29971aa65a72ead251bb3be5135b54a1a1c0a858e80959b877aea16975acb *electron-v25.4.0-linux-x64-symbols.zip +7cce96b1f33c7f6333d24f6966bd0613e6d95a9fd3e7fba4f20d11869adc22c6 *electron-v25.4.0-linux-x64.zip +615e880460216419d9b6dfa8cdf59d067ad3f629497e4419cd723351e544645a *electron-v25.4.0-win32-arm64-pdb.zip +548ac3cbfe5a4bd1c8628aa4a73cc424116c0870ab119cf8077993b932baf51b *electron-v25.4.0-win32-arm64-symbols.zip +c51677babba862a0b98ceee992b42eaf7d2c9e8e9dfb1496d15d7381b908dc72 *electron-v25.4.0-win32-arm64.zip +9a6271c5d9eaf07fb01b38bd743b2cdc0642bcf16f9add2724eccad2aea2b3a8 *electron-v25.4.0-win32-ia32-pdb.zip +e961dd15cfde46c9765eebd4f421e17850b85c3f25e0e6891b4f55a9834805f6 *electron-v25.4.0-win32-ia32-symbols.zip +14320e1f7db40e885ee101acd39698be58745cdfe6a519e0a5316269837f5bf7 *electron-v25.4.0-win32-ia32.zip +b055eb2b0bf037fc54f8af9d76c5894ffaffe6bd24e231da7ce847048ed60c97 *electron-v25.4.0-win32-x64-pdb.zip +0d84815a05dd0dd96266808d3cefde52cdea149c5faf2abb2d4d3c028e5f30d0 *electron-v25.4.0-win32-x64-symbols.zip +6f0c6da70a4daa50ec46c389896d08009ce14cdac5896c608e5af8956fd359de *electron-v25.4.0-win32-x64.zip +0e4fd74705d098d616b313b9078e74ba3b44ead2f3abd083acd8fab68048a563 *ffmpeg-v25.4.0-darwin-arm64.zip +a4c1b013448c5b9955d4927be7a72f8836a7af909c795aeecdb43086e76bd0d8 *ffmpeg-v25.4.0-darwin-x64.zip +bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.4.0-linux-arm64.zip +9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.4.0-linux-armv7l.zip +edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.4.0-linux-x64.zip +39d5389ff83fd30bacfd82e3beea5cb95eaa46aaa7d2ddaccc705f2490210f2e *ffmpeg-v25.4.0-win32-arm64.zip +059c72026437c4d97ec55ef6fab85526d2fa1de3571c478a55dec3489487fdf4 *ffmpeg-v25.4.0-win32-ia32.zip +01871604f273c3206c808954c604afda0294da15dd02dd6360fe736e5b5c82a8 *ffmpeg-v25.4.0-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index 5229a5bc80a..de9f8f07150 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -f9f02f7872e2e8ee54320fce13deb9d56904f32bb0615b6e21aa3371d8899150 node-v16.17.1-darwin-arm64.tar.gz -3db26761ad8493b894d42260d7e65094b7af9bc473588739e61bc1c32d6ff955 node-v16.17.1-darwin-x64.tar.gz -adc7032888d4e672a4aac886baede8c04fccdd1a2e7ab4bcf325e3f336f44a3d node-v16.17.1-linux-arm64.tar.gz -aeab05e35f1d2824ecfb88ca321f1408b44d292b2775f2890972c828e00216d0 node-v16.17.1-linux-armv7l.tar.gz -da5658693243b3ecf6a4cba6751a71df1eb9e9703ca93b42a9404aed85f58ad0 node-v16.17.1-linux-x64.tar.gz -f518a70dcab7c3fac5b2e1ef100b4f628edfb160f4fafa9a94ef222da8a6e9ab win-x64/node.exe -2393aff88be19dbe0205cbde4ff0c1d89911b15de5c99c80f6e5e29604eecd12 win-x86/node.exe +bd302a689c3c34e2b61d86b97de66d26a335881a17af09b6a0a4bb1019df56e4 node-v18.15.0-darwin-arm64.tar.gz +76add174d2d3f98da08907412e82add7352b8cb6f639324d352a65c084b99c7e node-v18.15.0-darwin-x64.tar.gz +8ef7aa7a679360ddbf0c7c8511881030b3de9d1f54451d92ba5c8d59a91c7073 node-v18.15.0-linux-arm64.tar.gz +ca2186313d3cbe5c67d0c08e931a6d290906f4f13c584e63fefa05a04dee9c58 node-v18.15.0-linux-armv7l.tar.gz +b298a73a9fc07badfa9e4a2e86ed48824fc9201327cdc43e3f3f58b273c535e7 node-v18.15.0-linux-x64.tar.gz +17fd75d8a41bf9b4c475143e19ff2808afa7a92f7502ede731537d9da674d5e8 win-x64/node.exe +d78b2f981465a40a23b964b2db32a390db1970a0dd5371682e121ae2b7422697 win-x86/node.exe 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.extensions.js b/build/gulpfile.extensions.js index e2c9e3d9aba..bcdb206606b 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -64,6 +64,7 @@ const compilations = [ 'search-result/tsconfig.json', 'references-view/tsconfig.json', 'simple-browser/tsconfig.json', + 'tunnel-forwarding/tsconfig.json', 'typescript-language-features/test-workspace/tsconfig.json', 'typescript-language-features/web/tsconfig.json', 'typescript-language-features/tsconfig.json', 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 2d6f14551b3..674eb41a503 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -98,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], diff --git a/build/lib/electron.js b/build/lib/electron.js index f06445d87fa..6d315b386ad 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -212,4 +212,4 @@ if (require.main === module) { process.exit(1); }); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWxlY3Ryb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJlbGVjdHJvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLGdDQUFnQztBQUNoQyxzQ0FBc0M7QUFDdEMsZ0NBQWdDO0FBQ2hDLCtCQUErQjtBQUMvQiw2Q0FBMEM7QUFZMUMsU0FBUyxnQkFBZ0IsQ0FBQyxHQUFZO0lBQ3JDLE9BQU8sR0FBRyxLQUFLLFVBQVUsSUFBSSxHQUFHLEtBQUssUUFBUSxJQUFJLEdBQUcsS0FBSyxNQUFNLElBQUksR0FBRyxLQUFLLGFBQWEsQ0FBQztBQUMxRixDQUFDO0FBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7QUFDbkQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGNBQWMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFDckYsTUFBTSxNQUFNLEdBQUcsSUFBQSx1QkFBVSxFQUFDLElBQUksQ0FBQyxDQUFDO0FBRWhDLE1BQU0scUJBQXFCLEdBQUcsT0FBTyxDQUFDLGFBQWEsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFFbkk7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNILFNBQVMsd0JBQXdCLENBQUMsVUFBb0IsRUFBRSxJQUFZLEVBQUUsWUFBNEMsRUFBRSxJQUFlO0lBQ2xJLDJGQUEyRjtJQUMzRixJQUFJLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsWUFB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWxlY3Ryb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJlbGVjdHJvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLGdDQUFnQztBQUNoQyxzQ0FBc0M7QUFDdEMsZ0NBQWdDO0FBQ2hDLCtCQUErQjtBQUMvQiw2Q0FBMEM7QUFZMUMsU0FBUyxnQkFBZ0IsQ0FBQyxHQUFZO0lBQ3JDLE9BQU8sR0FBRyxLQUFLLFVBQVUsSUFBSSxHQUFHLEtBQUssUUFBUSxJQUFJLEdBQUcsS0FBSyxNQUFNLElBQUksR0FBRyxLQUFLLGFBQWEsQ0FBQztBQUMxRixDQUFDO0FBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7QUFDbkQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGNBQWMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFDckYsTUFBTSxNQUFNLEdBQUcsSUFBQSx1QkFBVSxFQUFDLElBQUksQ0FBQyxDQUFDO0FBRWhDLE1BQU0scUJBQXFCLEdBQUcsT0FBTyxDQUFDLGFBQWEsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFFbkk7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNILFNBQVMsd0JBQXdCLENBQUMsVUFBb0IsRUFBRSxJQUFZLEVBQUUsWUFBNEMsRUFBRSxJQUFlO0lBQ2xJLDJGQUEyRjtJQUMzRixJQUFJLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsWUFB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 \ No newline at end of file diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 0c3df8cfd71..592708cc385 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -216,7 +216,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream { }; } -async function main(arch = process.arch): Promise { +async function main(arch: string = process.arch): Promise { const version = electronVersion; const electronPath = path.join(root, '.build', 'electron'); const versionFile = path.join(electronPath, 'version'); 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/layersChecker.js b/build/lib/layersChecker.js index f52680fd9da..618c736b30a 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -58,6 +58,11 @@ const CORE_TYPES = [ 'URL', 'URLSearchParams', 'ReadonlyArray', + 'Event', + 'EventTarget', + 'BroadcastChannel', + 'performance', + 'Blob' ]; // Types that are defined in a common layer but are known to be only // available in native environments should not be allowed in browser @@ -283,4 +288,4 @@ for (const sourceFile of program.getSourceFiles()) { if (hasErrors) { process.exit(1); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGF5ZXJzQ2hlY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImxheWVyc0NoZWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxpQ0FBaUM7QUFDakMsMkJBQThDO0FBQzlDLCtCQUE4QztBQUM5Qyx5Q0FBa0M7QUFFbEMsRUFBRTtBQUNGLGdHQUFnRztBQUNoRyxFQUFFO0FBQ0YsK0ZBQStGO0FBQy9GLG1EQUFtRDtBQUNuRCw0RUFBNEU7QUFDNUUsaUVBQWlFO0FBQ2pFLEVBQUU7QUFDRixnR0FBZ0c7QUFDaEcsRUFBRTtBQUNGLGdHQUFnRztBQUNoRyxFQUFFO0FBRUYsbUZBQW1GO0FBQ25GLHdGQUF3RjtBQUN4RixNQUFNLFVBQVUsR0FBRztJQUNsQixTQUFTO0lBQ1QsWUFBWTtJQUNaLGNBQWM7SUFDZCxhQUFhO0lBQ2IsZUFBZTtJQUNmLFNBQVM7SUFDVCxTQUFTO0lBQ1QsT0FBTztJQUNQLGtCQUFrQjtJQUNsQixRQUFRO0lBQ1IsYUFBYTtJQUNiLGFBQWE7SUFDYixNQUFNO0lBQ04sZ0JBQWdCO0lBQ2hCLE9BQU87SUFDUCxZQUFZO0lBQ1osYUFBYTtJQUNiLGFBQWE7SUFDYixXQUFXO0lBQ1gsWUFBWTtJQUNaLFlBQVk7SUFDWixjQUFjO0lBQ2QsY0FBYztJQUNkLG1CQUFtQjtJQUNuQixnQkFBZ0I7SUFDaEIsZUFBZTtJQUNmLE1BQU07SUFDTixNQUFNO0lBQ04saUJBQWlCO0lBQ2pCLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsYUFBYTtJQUNiLEtBQUs7SUFDTCxpQkFBaUI7SUFDakIsZUFBZTtDQUNmLENBQUM7QUFFRixvRUFBb0U7QUFDcEUsb0VBQW9FO0FBQ3BFLE1BQU0sWUFBWSxHQUFHO0lBQ3BCLGtCQUFrQjtJQUNsQiwyQkFBMkI7SUFDM0Isa0NBQWtDO0lBQ2xDLDRCQUE0QjtJQUM1QiwwQkFBMEI7SUFDMUIsb0JBQW9CO0lBQ3BCLHFCQUFxQjtDQUNyQixDQUFDO0FBRUYs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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGF5ZXJzQ2hlY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImxheWVyc0NoZWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxpQ0FBaUM7QUFDakMsMkJBQThDO0FBQzlDLCtCQUE4QztBQUM5Qyx5Q0FBa0M7QUFFbEMsRUFBRTtBQUNGLGdHQUFnRztBQUNoRyxFQUFFO0FBQ0YsK0ZBQStGO0FBQy9GLG1EQUFtRDtBQUNuRCw0RUFBNEU7QUFDNUUsaUVBQWlFO0FBQ2pFLEVBQUU7QUFDRixnR0FBZ0c7QUFDaEcsRUFBRTtBQUNGLGdHQUFnRztBQUNoRyxFQUFFO0FBRUYsbUZBQW1GO0FBQ25GLHdGQUF3RjtBQUN4RixNQUFNLFVBQVUsR0FBRztJQUNsQixTQUFTO0lBQ1QsWUFBWTtJQUNaLGNBQWM7SUFDZCxhQUFhO0lBQ2IsZUFBZTtJQUNmLFNBQVM7SUFDVCxTQUFTO0lBQ1QsT0FBTztJQUNQLGtCQUFrQjtJQUNsQixRQUFRO0lBQ1IsYUFBYTtJQUNiLGFBQWE7SUFDYixNQUFNO0lBQ04sZ0JBQWdCO0lBQ2hCLE9BQU87SUFDUCxZQUFZO0lBQ1osYUFBYTtJQUNiLGFBQWE7SUFDYixXQUFXO0lBQ1gsWUFBWTtJQUNaLFlBQVk7SUFDWixjQUFjO0lBQ2QsY0FBYztJQUNkLG1CQUFtQjtJQUNuQixnQkFBZ0I7SUFDaEIsZUFBZTtJQUNmLE1BQU07SUFDTixNQUFNO0lBQ04saUJBQWlCO0lBQ2pCLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsYUFBYTtJQUNiLEtBQUs7SUFDTCxpQkFBaUI7SUFDakIsZUFBZTtJQUNmLE9BQU87SUFDUCxhQUFhO0lBQ2Isa0JBQWtCO0lBQ2xCLGFBQWE7SUFDYixNQUFNO0NBQ04sQ0FBQztBQUVGLG9FQUFvRTtBQUNwRSxvRUFBb0U7QUFDcEUsTUFBTSxZQUFZLEdBQUc7SUFDcEIsa0JBQWtCO0lBQ2xCLDJCQUEyQjtJQUMzQixrQ0FBa0M7SUFDbEMsNEJBQTRCO0lB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 \ No newline at end of file diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 95f88735bf8..e7fdfd4214e 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -59,6 +59,11 @@ const CORE_TYPES = [ 'URL', 'URLSearchParams', 'ReadonlyArray', + 'Event', + 'EventTarget', + 'BroadcastChannel', + 'performance', + 'Blob' ]; // Types that are defined in a common layer but are known to be only diff --git a/build/lib/mangle/index.js b/build/lib/mangle/index.js index 75981c79d06..27281954b74 100644 --- a/build/lib/mangle/index.js +++ b/build/lib/mangle/index.js @@ -246,12 +246,6 @@ function isNameTakenInFile(node, name) { } return false; } -const fileIdents = new class { - idents = new ShortIdent('$'); - next() { - return this.idents.next(); - } -}; const skippedExportMangledFiles = [ // Build 'css.build', @@ -301,7 +295,7 @@ class DeclarationData { node; service; replacementName; - constructor(fileName, node, service) { + constructor(fileName, node, service, fileIdents) { this.fileName = fileName; this.node = node; this.service = service; @@ -368,6 +362,7 @@ class Mangler { // STEP: // - Find all classes and their field info. // - Find exported symbols. + const fileIdents = new ShortIdent('$'); const visit = (node) => { if (this.config.manglePrivateFields) { if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) { @@ -410,7 +405,7 @@ class Mangler { if (isInAmbientContext(node)) { return; } - this.allExportedSymbols.add(new DeclarationData(node.getSourceFile().fileName, node, this.service)); + this.allExportedSymbols.add(new DeclarationData(node.getSourceFile().fileName, node, this.service, fileIdents)); } } ts.forEachChild(node, visit); @@ -662,4 +657,4 @@ async function _run() { if (__filename === process_1.argv[1]) { _run(); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLHFDQUErQjtBQUMvQiwyQ0FBeUQ7QUFDekQsaUNBQWlDO0FBQ2pDLDZCQUFvQztBQUNwQyx5Q0FBeUM7QUFDekMsMkVBQXdFO0FBQ3hFLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO0FBRXBELE1BQU0sVUFBVTtJQVlHO0lBVlYsTUFBTSxDQUFDLFNBQVMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVO1FBQzlHLFNBQVMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxJQUFJO1FBQ25HLFFBQVEsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsT0FBTztRQUMxRyxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUU1RCxNQUFNLENBQUMsU0FBUyxHQUFHLGtFQUFrRSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUVoRyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBRW5CLFlBQ2tCLE1BQWM7UUFBZCxXQUFNLEdBQU4sTUFBTSxDQUFRO0lBQzVCLENBQUM7SUFFTCxJQUFJLENBQUMsV0FBdUM7UUFDM0MsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNoRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDZCxJQUFJLFVBQVUsQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksV0FBVyxFQUFFLENBQUMsU0FBUyxDQUFDLEVBQUU7WUFDakcsWUFBWTtZQUNaLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztTQUM5QjtRQUNELE9BQU8sU0FBUyxDQUFDO0lBQ2xCLENBQUM7SUFFTyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQVM7UUFDL0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUM7UUFDbkMsSUFBSSxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBQ2hCLEdBQUc7WUFDRixNQUFNLElBQUksR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDO1lBQ3RCLE1BQU0sSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQy9CLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDbkIsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFO1FBQ2hCLE9BQU8sTUFBTSxDQUFDO0lBQ2YsQ0FBQzs7QUFHRixJQUFXLFNBSVY7QUFKRCxXQUFXLFNBQVM7SUFDbkIsNkNBQU0sQ0FBQTtJQUNOLG1EQUFTLENBQUE7SUFDVCwrQ0FBTyxDQUFBO0FBQ1IsQ0FBQyxFQUpVLFNBQVMsS0FBVCxTQUFTLFFBSW5CO0FBRUQsTUFBTSxTQUFTO0lBVUo7SUFDQTtJQVRWLE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFBNEMsQ0FBQztJQUVyRCxZQUFZLENBQWtDO0lBRXRELE1BQU0sQ0FBd0I7SUFDOUIsUUFBUSxDQUEwQjtJQUVsQyxZQUNVLFFBQWdCLEVBQ2hCLElBQThDO1FBRXZELGdGQUFnRjtRQUNoRixnRkFBZ0Y7UUFKdkUsYUFBUSxHQUFSLFFBQVEsQ0FBUTtRQUNoQixTQUFJLEdBQUosSUFBSSxDQUEwQztRQUt2RCxNQUFNLFVBQVUsR0FBNEIsRUFBRSxDQUFDO1FBQy9DLEtBQUssTUFBTSxNQUFNLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNsQyxJQUFJLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsRUFBRTtnQkFDbkMsb0JBQW9CO2dCQUNwQixVQUFVLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2FBRXhCO2lCQUFNLElBQUksRUFBRSxDQUFDLHFCQUFxQixDQUFDLE1BQU0sQ0FBQyxFQUFFO2dCQUM1Qyx1QkFBdUI7Z0JBQ3ZCLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7YUFFeEI7aUJBQU0sSUFBSSxFQUFFLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxFQUFFO2dCQUNwQyw4QkFBOEI7Z0JBQzlCLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7YUFFeEI7aUJBQU0sSUFBSSxFQUFFLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxFQUFFO2dCQUNwQyw4QkFBOEI7Z0JBQzlCLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7YUFFeEI7aUJBQU0sSUFBSSxFQUFFLENBQUMsd0JBQXdCLENBQUMsTUFBTSxDQUFDLEVBQUU7Z0JBQy9DLGlEQUFpRDtnQkFDakQsS0FBSyxNQUFNLEtBQUssSUFBSSxNQUFNLENBQUMsVUFBVSxFQUFFO29CQUN0QyxJQUFJLFdBQVcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUM7MkJBQ2hELFdBQVcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQzsyQkFDbEQsV0FBVyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQzsyQkFDL0MsV0FBVyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FBQyxFQUNuRDt3QkFDRCxVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO3FCQUN2QjtpQkFDRDthQUNEO1NBQ0Q7UUFDRCxLQUFLLE1BQU0sTUFBTSxJQUFJLFVBQVUsRUFBRTtZQUNoQyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQy9DLElBQUksQ0FBQyxLQUFLLEVBQUU7Z0JBQ1gsU0FBUzthQUNUO1lBQ0QsTUFBTSxJQUFJLEdBQUcsU0FBUyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUM3QyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sQ0FBQyxJQUFLLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1NBQy9EO0lBQ0YsQ0FBQztJQUVPLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBeUI7UUFDdEQsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDZixPQUFPLFNBQVMsQ0FBQztTQUNqQjtRQUNELE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFDdEIsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQzNCLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxFQUFFLENBQUMsVUFBVSxDQUFDLG9CQUFvQixFQUFFO1lBQ3JELElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEtBQUssRUFBRSxDQUFDLFVBQVUsQ0FBQyxhQUFhLEVBQUU7Z0JBQ3pELCtDQUErQztnQkFDL0MsT0FBTzthQUNQO1lBQ0QsVUFBVTtZQUNWLEtBQUssR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUMvQztRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQUVPLE1BQU0sQ0FBQyxhQUFhLENBQUMsSUFBYTtRQUN6QyxJQUFJLFdBQVcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsRUFBRTtZQUNwRCxpQ0FBeUI7U0FDekI7YUFBTSxJQUFJLFdBQVcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFO1lBQzdELG1DQUEyQjtTQUMzQjthQUFNO1lBQ04sZ0NBQXdCO1NBQ3hCO0lBQ0YsQ0FBQztJQUVELE1BQU0sQ0FBQyxhQUFhLENBQUMsSUFBZTtRQUNuQyxPQUFPLElBQUksOEJBQXNCO2VBQzdCLElBQUksZ0NBQXdCLENBQzlCO0lBQ0gsQ0FBQztJQUVELE1BQU0sQ0FBQyxnQ0FBZ0MsQ0FBQyxJQUFlLEVBQUUsZUFBa0U7UUFDMUgsVUFBVTtRQUNWLGlGQUFpRjtRQUNqRixpRkFBaUY7UUFDakYsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDdkMsSUFBSSxJQUFJLENBQUMsSUFBSSw2QkFBcUIsRUFBRTtnQkFDbkMsU0FBUzthQUNUO1lBQ0QsSUFBSSxNQUFNLEdBQTBCLElBQUksQ0FBQyxNQUFNLENBQUM7WUFDaEQsT0FBTyxNQUFNLEVBQUU7Z0JBQ2QsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLGdDQUF3QixFQUFFO29CQUMxRCxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLDZCQUE2QixDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUMxRyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLDZCQUE2QixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDbEYsZUFBZSxDQUFDLElBQUksRUFBRSxJQUFJLElBQUksVUFBVSxNQUFNLENBQUMsUUFBUSxJQUFJLFNBQVMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxJQUFJLENBQUMsUUFBUSxJQUFJLE9BQU8sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztvQkFFekgsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFFLENBQUMsSUFBSSwyQkFBbUIsQ0FBQztpQkFDakQ7Z0JBQ0QsTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7YUFDdkI7U0FDRDtJQUNGLENBQUM7SUFFRCxNQUFNLENBQUMsaUJBQWlCLENBQUMsSUFBZTtRQUV2QyxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7WUFDdEIsZUFBZTtZQUNmLE9BQU87U0FDUDtRQUVELHdCQUF3QjtRQUN4QixJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDaEIsU0FBUyxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUN6QztRQUVELElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUU5QixNQUFNLFdBQVcsR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFO1lBQ3BDLGdCQUFnQjtZQUNoQixJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQzVCLE9BQU8sSUFBSSxDQUFDO2FBQ1o7WUFFRCxVQUFVO1lBQ1YsSUFBSSxNQUFNLEdBQTBCLElBQUksQ0FBQyxNQUFNLENBQUM7WUFDaEQsT0FBTyxNQUFNLEVBQUU7Z0JBQ2QsSUFBSSxNQUFNLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUM5QixPQUFPLElBQUksQ0FBQztpQkFDWjtnQkFDRCxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQzthQUN2QjtZQUVELFdBQVc7WUFDWCxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUU7Z0JBQ2xCLE1BQU0sS0FBSyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQ2pDLE9BQU8sS0FBSyxDQUFDLE1BQU0sRUFBRTtvQkFDcEIsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLEdBQUcsRUFBRyxDQUFDO29CQUMxQixJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLEVBQUU7d0JBQzVCLE9BQU8sSUFBSSxDQUFDO3FCQUNaO29CQUNELElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTt3QkFDbEIsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztxQkFDN0I7aUJBQ0Q7YUFDRDtZQUVELE9BQU8sS0FBSyxDQUFDO1FBQ2QsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxTQUFTLEdBQUcsSUFBSSxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFckMsS0FBSyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUU7WUFDdkMsSUFBSSxTQUFTLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDdkMsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDOUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDO2FBQ3ZDO1NBQ0Q7SUFDRixDQUFDO0lBRUQsa0VBQWtFO0lBQ2xFLGtEQUFrRDtJQUMxQyxZQUFZLENBQUMsSUFBWTtRQUNoQyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtZQUNuRixlQUFlO1lBQ2YsT0FBTyxJQUFJLENBQUM7U0FDWjtRQUNELElBQUksSUFBSSxDQUFDLFlBQVksRUFBRTtZQUN0QixLQUFLLE1BQU0sU0FBUyxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLEVBQUU7Z0JBQ25ELElBQUksU0FBUyxLQUFLLElBQUksRUFBRTtvQkFDdkIsNkNBQTZDO29CQUM3QyxPQUFPLElBQUksQ0FBQztpQkFDWjthQUNEO1NBQ0Q7UUFFRCxJQUFJLGlCQUFpQixDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDdkMsT0FBTyxJQUFJLENBQUM7U0FDWjtRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQUVELGVBQWUsQ0FBQyxJQUFZO1FBQzNCLElBQUksS0FBSyxHQUFHLElBQUksQ0FBQyxZQUFhLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBRSxDQUFDO1FBQzFDLElBQUksTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDekIsT0FBTyxNQUFNLEVBQUU7WUFDZCxJQUFJLE1BQU0sQ0FBQyxZQUFhLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksZ0NBQXdCLEVBQUU7Z0JBQzVGLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBYSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUUsSUFBSSxLQUFLLENBQUM7YUFDakQ7WUFDRCxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztTQUN2QjtRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2QsQ0FBQztJQUVELHNCQUFzQjtJQUV0QixRQUFRLENBQUMsS0FBZ0I7UUFDeEIsSUFBSSxDQUFDLFFBQVEsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDMUIsS0FBSyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDckIsQ0FBQztDQUNEO0FBRUQsU0FBUyxpQkFBaUIsQ0FBQyxJQUFhLEVBQUUsSUFBWTtJQUNyRCxNQUFNLFdBQVcsR0FBUyxJQUFJLENBQUMsYUFBYSxFQUFHLENBQUMsV0FBVyxDQUFDO0lBQzVELElBQUksV0FBVyxZQUFZLEdBQUcsRUFBRTtRQUMvQixJQUFJLFdBQVcsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDMUIsT0FBTyxJQUFJLENBQUM7U0FDWjtLQUNEO0lBQ0QsT0FBTyxLQUFLLENBQUM7QUFDZCxDQUFDO0FBRUQsTUFBTSxVQUFVLEdBQUcsSUFBSTtJQUNMLE1BQU0sR0FBRyxJQUFJLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUU5QyxJQUFJO1FBQ0gsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQzNCLENBQUM7Q0FDRCxDQUFDO0FBRUYsTUFBTSx5QkFBeUIsR0FBRztJQUNqQyxRQUFRO0lBQ1IsV0FBVztJQUNYLFdBQVc7SUFFWCxTQUFTO0lBQ1QsY0FBYztJQUNkLGVBQWU7SUFDZixZQUFZO0lBQ1osa0JBQWtCO0lBQ2xCLGlCQUFpQjtJQUNqQixxQkFBcUI7SUFFckIsWUFBWTtJQUNaLHdCQUF3QjtJQUV4QiwrQkFBK0I7SUFDL0IsS0FBSztJQUVMLGVBQWU7SUFDZixHQUFHO1FBQ0YsU0FBUyxDQUFDLFVBQVUsQ0FBQyw0QkFBNEIsRUFBRSxFQUFFLENBQUM7UUFDdEQsU0FBUyxDQUFDLFVBQVUsQ0FBQyxxQ0FBcUMsRUFBRSxFQUFFLENBQUM7UUFDL0QsU0FBUyxDQUFDLElBQUk7UUFDZCxTQUFTLENBQUMsbUJBQW1CO1FBQzdCLFNBQVMsQ0FBQyxjQUFjO1FBQ3hCLFNBQVMsQ0FBQyx1QkFBdUI7UUFDakMsU0FBUyxDQUFDLHFCQUFxQjtRQUMvQixTQUFTLENBQUMscUJBQXFCO1FBQy9CLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDMUIsU0FBUyxDQUFDLFlBQVk7UUFDdEIsU0FBUyxDQUFDLElBQUk7S0FDZCxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7Q0FDekIsQ0FBQztBQUVGLE1BQU0sNEJBQTRCLEdBQUc7SUFDcEMsZ0JBQWdCO0lBQ2hCLGtCQUFrQjtJQUVsQiwwRkFBMEY7SUFDMUYsdUJBQXVCO0lBQ3ZCLDBCQUEwQjtJQUMxQix1QkFBdUI7SUFDdkIsK0JBQStCO0NBQy9CLENBQUM7QUFFRixNQUFNLDJCQUEyQixHQUFHO0lBQ25DLHNDQUFzQztJQUN0QyxVQUFVO0lBQ1YsWUFBWTtDQUNaLENBQUM7QUFFRixNQUFNLGVBQWU7SUFLVjtJQUNBO0lBQ1E7SUFMVCxlQUFlLENBQVM7SUFFakMsWUFDVSxRQUFnQixFQUNoQixJQUFnRyxFQUN4RixPQUEyQjtRQUZuQyxhQUFRLEdBQVIsUUFBUSxDQUFRO1FBQ2hCLFNBQUksR0FBSixJQUFJLENBQTRGO1FBQ3hGLFlBQU8sR0FBUCxPQUFPLENBQW9CO1FBRTVDLDBHQUEwRztRQUMxRyxJQUFJLENBQUMsZUFBZSxHQUFHLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUMxQyxDQUFDO0lBRUQsSUFBSSxTQUFTO1FBQ1osSUFBSSxFQUFFLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ3hDLDhEQUE4RDtZQUM5RCxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMseUJBQXlCLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBQzFHLElBQUksZ0JBQWdCLEVBQUUsV0FBVyxJQUFJLGdCQUFnQixDQUFDLFdBQVcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO2dCQUM3RSxPQUFPLGdCQUFnQixDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO2FBQ25HO1NBQ0Q7UUFFRCxPQUFPLENBQUM7Z0JBQ1AsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO2dCQUN2QixNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFLLENBQUMsUUFBUSxFQUFFO2FBQ2xDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxZQUFZLENBQUMsT0FBZTtRQUMzQixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUM5QyxJQUFJLFdBQVcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksMkJBQTJCLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFFO1lBQ3JGLE9BQU8sS0FBSyxDQUFDO1NBQ2I7UUFFRCwwQ0FBMEM7UUFDMUMsSUFBSSxPQUFPLENBQUMsTUFBTSxJQUFJLFdBQVcsQ0FBQyxNQUFNLEVBQUU7WUFDekMsT0FBTyxLQUFLLENBQUM7U0FDYjtRQUVELG9EQUFvRDtRQUNwRCxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxFQUFFO1lBQ3BELE9BQU8sS0FBSyxDQUFDO1NBQ2I7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNiLENBQUM7Q0FDRDtBQU9EOzs7Ozs7OztHQVFHO0FBQ0gsTUFBYSxPQUFPO0lBU0Q7SUFDQTtJQUNBO0lBVEQsaUJBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQXFCLENBQUM7SUFDakQsa0JBQWtCLEdBQUcsSUFBSSxHQUFHLEVBQW1CLENBQUM7SUFFaEQsT0FBTyxDQUFxQjtJQUM1QixnQkFBZ0IsQ0FBd0I7SUFFekQsWUFDa0IsV0FBbUIsRUFDbkIsTUFBMEIsR0FBRyxFQUFFLEdBQUcsQ0FBQyxFQUNuQyxNQUFrRjtRQUZsRixnQkFBVyxHQUFYLFdBQVcsQ0FBUTtRQUNuQixRQUFHLEdBQUgsR0FBRyxDQUFnQztRQUNuQyxXQUFNLEdBQU4sTUFBTSxDQUE0RTtRQUVuRyxJQUFJLENBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLHFEQUF5QixDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7UUFFcEYsSUFBSSxDQUFDLGdCQUFnQixHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsaUJBQWlCLENBQUMsRUFBRTtZQUNoRixVQUFVLEVBQUUsQ0FBQztZQUNiLFVBQVUsRUFBRSxLQUFLO1NBQ2pCLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsc0JBQXNCLENBQUMsNEJBQTBDO1FBRXRFLFFBQVE7UUFDUiwyQ0FBMkM7UUFDM0MsMkJBQTJCO1FBRTNCLE1BQU0sS0FBSyxHQUFHLENBQUMsSUFBYSxFQUFRLEVBQUU7WUFDckMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLG1CQUFtQixFQUFFO2dCQUNwQyxJQUFJLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQUU7b0JBQzlELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDO29CQUNqQyxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQyxRQUFRLElBQUksTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUM7b0JBQ3BFLElBQUksSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRTt3QkFDcEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztxQkFDekI7b0JBQ0QsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxTQUFTLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO2lCQUNwRjthQUNEO1lBRUQsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRTtnQkFDOUIsNkNBQTZDO2dCQUM3QyxJQUNDO2dCQUNDLGlCQUFpQjtnQkFDakIsRUFBRSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQzt1QkFDeEIsV0FBVyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQzt1QkFDOUMsSUFBSSxDQUFDLElBQUksQ0FDWixJQUFJO2dCQUNKLG9CQUFvQjtnQkFDcEIsRUFBRSxDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQzt1QkFDM0IsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO3VCQUM1QixXQUFXLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDO3VCQUM5QyxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsNENBQTRDO2lCQUN0RSxJQUFJO2dCQUNKLG9CQUFvQjtnQkFDcEIsRUFBRSxDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQzt1QkFDM0IsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUMsaUNBQWlDO3VCQUM5RixFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUM3QztnQkFFRCwrREFBK0Q7Z0JBQy9ELHVEQUF1RDtnQkFDdkQ7Ozs7Ozs7a0JBT0U7a0JBQ0Q7b0JBQ0QsSUFBSSxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsRUFBRTt3QkFDN0IsT0FBTztxQkFDUDtvQkFFRCxJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFDLElBQUksZUFBZSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO2lCQUNwRzthQUNEO1lBRUQsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDOUIsQ0FBQyxDQUFDO1FBRUYsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRyxDQUFDLGNBQWMsRUFBRSxFQUFFO1lBQy9ELElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUU7Z0JBQzVCLEVBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDO2FBQzdCO1NBQ0Q7UUFDRCxJQUFJLENBQUMsR0FBRyxDQUFDLDZCQUE2QixJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSx1QkFBdUIsSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFHeEgscUNBQXFDO1FBRXJDLE1BQU0sWUFBWSxHQUFHLENBQUMsSUFBZSxFQUFFLEVBQUU7WUFDeEMsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssS0FBSyxFQUFFLENBQUMsVUFBVSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQ3JHLElBQUksQ0FBQyxhQUFhLEVBQUU7Z0JBQ25CLG9CQUFvQjtnQkFDcEIsT0FBTzthQUNQO1lBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDN0csSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtnQkFDL0IsMkNBQTJDO2dCQUMzQyxPQUFPO2FBQ1A7WUFFRCxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO2dCQUN0QixzQ0FBc0M7Z0JBQ3RDLE9BQU87YUFDUDtZQUVELE1BQU0sQ0FBQyxVQUFVLENBQUMsR0FBRyxJQUFJLENBQUM7WUFDMUIsTUFBTSxHQUFHLEdBQUcsR0FBRyxVQUFVLENBQUMsUUFBUSxJQUFJLFVBQVUsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDbEUsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMvQyxJQUFJLENBQUMsTUFBTSxFQUFFO2dCQUNaLG1EQUFtRDtnQkFDbkQsT0FBTzthQUNQO1lBQ0QsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QixDQUFDLENBQUM7UUFDRixLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUNuRCxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDbkI7UUFFRCx1RUFBdUU7UUFDdkUsTUFBTSxVQUFVLEdBQUcsSUFBSSxHQUFHLEVBQW9CLENBQUM7UUFDL0MsSUFBSSxzQkFBc0IsR0FBRyxLQUFLLENBQUM7UUFDbkMsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDbkQsU0FBUyxDQUFDLGdDQUFnQyxDQUFDLElBQUksRUFBRSxDQUFDLElBQVksRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLEVBQUU7Z0JBQzVFLE1BQU0sR0FBRyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ2pDLElBQUksR0FBRyxFQUFFO29CQUNSLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7aUJBQ2Q7cUJBQU07b0JBQ04sVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2lCQUM1QjtnQkFFRCxJQUFJLDRCQUE0QixJQUFJLENBQUMsNEJBQTRCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUM1RSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7aUJBQzlCO1lBQ0YsQ0FBQyxDQUFDLENBQUM7U0FDSDtRQUNELEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsSUFBSSxVQUFVLEVBQUU7WUFDckMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEdBQUcsOEJBQThCLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1NBQ3ZFO1FBQ0QsSUFBSSxzQkFBc0IsRUFBRTtZQUMzQixNQUFNLE9BQU8sR0FBRyxzSUFBc0ksQ0FBQztZQUN2SixJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUM5QixNQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQ3pCO1FBRUQsaURBQWlEO1FBQ2pELEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxFQUFFO1lBQ25ELFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNsQztRQUNELElBQUksQ0FBQyxHQUFHLENBQUMsa0NBQWtDLENBQUMsQ0FBQztRQUU3Qyw2QkFBNkI7UUFDN0IsSUFBSSxDQUFDLEdBQUcsQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO1FBRzFDLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1FBRTlDLE1BQU0sVUFBVSxHQUFHLENBQUMsUUFBZ0IsRUFBRSxJQUFVLEVBQUUsRUFBRTtZQUNuRCxNQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ3hDLElBQUksQ0FBQyxLQUFLLEVBQUU7Z0JBQ1gsV0FBVyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2FBQ2xDO2lCQUFNO2dCQUNOLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7YUFDakI7UUFDRixDQUFDLENBQUM7UUFDRixNQUFNLFlBQVksR0FBRyxDQUFDLE9BQWUsRUFBRSxHQUFzQixFQUFFLEVBQUU7WUFDaEUsVUFBVSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUU7Z0JBQ3hCLE9BQU8sRUFBRSxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLEdBQUcsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUM7Z0JBQ2xFLE1BQU0sRUFBRSxHQUFHLENBQUMsUUFBUSxDQUFDLEtBQUs7Z0JBQzFCLE1BQU0sRUFBRSxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU07YUFDM0IsQ0FBQyxDQUFDO1FBQ0osQ0FBQyxDQUFDO1FBSUYsTUFBTSxhQUFhLEdBQW1HLEVBQUUsQ0FBQztRQUV6SCxNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQWdCLEVBQUUsR0FBVyxFQUFFLE9BQWUsRUFBRSxFQUFFO1lBQ3RFLGFBQWEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFXLHFCQUFxQixFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztpQkFDaEksSUFBSSxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2xELENBQUMsQ0FBQztRQUVGLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxFQUFFO1lBQ25ELElBQUksV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsRUFBRTtnQkFDekQsU0FBUzthQUNUO1lBRUQsTUFBTSxFQUFFLEtBQUssTUFBTSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFO2dCQUMvQyxJQUFJLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUU7b0JBQ3hDLFNBQVMsTUFBTSxDQUFDO2lCQUNoQjtnQkFFRCxvREFBb0Q7Z0JBQ3BELHVEQUF1RDtnQkFDdkQsSUFBSSxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztnQkFDekIsT0FBTyxNQUFNLEVBQUU7b0JBQ2QsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLDZCQUFxQixFQUFFO3dCQUN2RCxTQUFTLE1BQU0sQ0FBQztxQkFDaEI7b0JBQ0QsTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7aUJBQ3ZCO2dCQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzNDLFdBQVcsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7YUFDOUM7U0FDRDtRQUVELEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sRUFBRSxFQUFFO1lBQ3BELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDO21CQUMvQiw0QkFBNEIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQzttQkFDdkUseUJBQXlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxHQUFHLEtBQUssQ0FBQyxDQUFDLEVBQzlFO2dCQUNELFNBQVM7YUFDVDtZQUVELElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsRUFBRTtnQkFDN0MsU0FBUzthQUNUO1lBRUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQztZQUNyQyxLQUFLLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRTtnQkFDbEQsV0FBVyxDQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7YUFDdkM7U0FDRDtRQUVELE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUNoRCxLQUFLLE1BQU0sRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLElBQUksTUFBTSxFQUFFO2dCQUM1QyxLQUFLLE1BQU0sR0FBRyxJQUFJLFNBQVMsRUFBRTtvQkFDNUIsWUFBWSxDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQztpQkFDM0I7YUFDRDtRQUNGLENBQUMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUM7UUFFeEMsSUFBSSxDQUFDLEdBQUcsQ0FBQyx5QkFBeUIsV0FBVyxDQUFDLElBQUksUUFBUSxDQUFDLENBQUM7UUFFNUQsMENBQTBDO1FBQzFDLE1BQU0sTUFBTSxHQUFHLElBQUksR0FBRyxFQUF3QixDQUFDO1FBQy9DLElBQUksVUFBVSxHQUFHLENBQUMsQ0FBQztRQUVuQixLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFHLENBQUMsY0FBYyxFQUFFLEVBQUU7WUFFL0QsTUFBTSxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRyxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDaEYsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDbEQsTUFBTSxhQUFhLEdBQUcsT0FBTyxJQUFJLElBQUEsbUJBQWEsRUFBQyxVQUFVLElBQUksVUFBVSxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUM7WUFFcEYsY0FBYztZQUNkLElBQUksU0FBeUMsQ0FBQztZQUU5QyxJQUFJLFdBQW1CLENBQUM7WUFDeEIsTUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDN0MsSUFBSSxDQUFDLEtBQUssRUFBRTtnQkFDWCxZQUFZO2dCQUNaLFdBQVcsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7YUFFakM7aUJBQU07Z0JBQ04sdUJBQXVCO2dCQUN2QixNQUFNLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLEVBQXFCLENBQUM7Z0JBRXBELGdCQUFnQjtnQkFDaEIsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUMxQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUVoRCxJQUFJLFFBQTBCLENBQUM7Z0JBRS9CLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFO29CQUN6QixJQUFJLFFBQVEsSUFBSSxRQUFRLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxNQUFNLEVBQUU7d0JBQ2hELEVBQUU7d0JBQ0YsSUFBSSxRQUFRLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxNQUFNLElBQUksUUFBUSxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsT0FBTyxFQUFFOzRCQUN6RSxJQUFJLENBQUMsR0FBRyxDQUFDLHlCQUF5QixFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQzs0QkFDdkUsTUFBTSxJQUFJLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO3lCQUNwQzs2QkFBTTs0QkFDTixTQUFTO3lCQUNUO3FCQUNEO29CQUNELFFBQVEsR0FBRyxJQUFJLENBQUM7b0JBQ2hCLE1BQU0sV0FBVyxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQ3ZGLFVBQVUsSUFBSSxXQUFXLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO29CQUV2RCxjQUFjO29CQUNkLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyw2QkFBNkIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBRzVELElBQUksUUFBUSxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUM1QyxJQUFJLENBQUMsUUFBUSxFQUFFO3dCQUNkLFFBQVEsR0FBRyxFQUFFLENBQUM7d0JBQ2QsY0FBYyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO3FCQUN2QztvQkFDRCxRQUFRLENBQUMsT0FBTyxDQUFDO3dCQUNoQixNQUFNLEVBQUUsZ0JBQWdCO3dCQUN4QixRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQyxTQUFTLEVBQUU7d0JBQ3ZELFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSSxHQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLFNBQVMsRUFBRTt3QkFDeEQsSUFBSSxFQUFFLFdBQVc7cUJBQ2pCLEVBQUU7d0JBQ0YsTUFBTSxFQUFFLGdCQUFnQjt3QkFDeEIsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUU7d0JBQ3JFLFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSSxHQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRTtxQkFDOUUsQ0FBQyxDQUFDO2lCQUNIO2dCQUVELG9FQUFvRTtnQkFDcEUsU0FBUyxHQUFHLElBQUksK0JBQWtCLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7Z0JBQ3RHLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQztnQkFDakUsS0FBSyxNQUFNLENBQUMsRUFBRSxRQUFRLENBQUMsSUFBSSxjQUFjLEVBQUU7b0JBQzFDLElBQUksU0FBUyxHQUFHLENBQUMsQ0FBQztvQkFDbEIsS0FBSyxNQUFNLE9BQU8sSUFBSSxRQUFRLEVBQUU7d0JBQy9CLFNBQVMsQ0FBQyxVQUFVLENBQUM7NEJBQ3BCLEdBQUcsT0FBTzs0QkFDVixTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLFNBQVMsRUFBRTt5QkFDekYsQ0FBQyxDQUFDO3dCQUNILFNBQVMsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQztxQkFDaEU7aUJBQ0Q7Z0JBRUQsV0FBVyxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDbEM7WUFDRCxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRSxHQUFHLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1NBQ2xGO1FBRUQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLFVBQVUsR0FBRyxJQUFJLFVBQVUsQ0FBQyxDQUFDO1FBQy9DLE9BQU8sTUFBTSxDQUFDO0lBQ2YsQ0FBQztDQUNEO0FBeFVELDBCQXdVQztBQUVELGdCQUFnQjtBQUVoQixTQUFTLFdBQVcsQ0FBQyxJQUFhLEVBQUUsSUFBbUI7SUFDdEQsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDaEYsT0FBTyxPQUFPLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQztBQUM3RCxDQUFDO0FBRUQsU0FBUyxrQkFBa0IsQ0FBQyxJQUFhO0lBQ3hDLEtBQUssSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFNLEVBQUU7UUFDMUMsSUFBSSxFQUFFLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDOUIsT0FBTyxJQUFJLENBQUM7U0FDWjtLQUNEO0lBQ0QsT0FBTyxLQUFLLENBQUM7QUFDZCxDQUFDO0FBRUQsU0FBUyxTQUFTLENBQUMsSUFBWTtJQUM5QixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQ2pDLENBQUM7QUFFRCxLQUFLLFVBQVUsSUFBSTtJQUNsQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3BELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzNDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQzVELE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDO0lBRTlGLEVBQUUsQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLGNBQWMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRTVELE1BQU0sT0FBTyxHQUFHLElBQUksT0FBTyxDQUFDLFdBQVcsRUFBRSxPQUFPLENBQUMsR0FBRyxFQUFFO1FBQ3JELGFBQWEsRUFBRSxJQUFJO1FBQ25CLG1CQUFtQixFQUFFLElBQUk7S0FDekIsQ0FBQyxDQUFDO0lBQ0gsS0FBSyxNQUFNLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxJQUFJLE1BQU0sT0FBTyxDQUFDLHNCQUFzQixDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFO1FBQ2hHLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDcEYsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDeEUsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZELElBQUksUUFBUSxDQUFDLFNBQVMsRUFBRTtZQUN2QixNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLFdBQVcsR0FBRyxNQUFNLEVBQUUsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1NBQ3RFO0tBQ0Q7QUFDRixDQUFDO0FBRUQsSUFBSSxVQUFVLEtBQUssY0FBSSxDQUFDLENBQUMsQ0FBQyxFQUFFO0lBQzNCLElBQUksRUFBRSxDQUFDO0NBQ1AifQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLHFDQUErQjtBQUMvQiwyQ0FBeUQ7QUFDekQsaUNBQWlDO0FBQ2pDLDZCQUFvQztBQUNwQyx5Q0FBeUM7QUFDekMsMkVBQXdFO0FBQ3hFLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO0FBRXBELE1BQU0sVUFBVTtJQVlHO0lBVlYsTUFBTSxDQUFDLFNBQVMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVO1FBQzlHLFNBQVMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxJQUFJO1FBQ25HLFFBQVEsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsT0FBTztRQUMxRyxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUU1RCxNQUFNLENBQUMsU0FBUyxHQUFHLGtFQUFrRSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUVoRyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBRW5CLFlBQ2tCLE1BQWM7UUFBZCxXQUFNLEdBQU4sTUFBTSxDQUFRO0lBQzVCLENBQUM7SUFFTCxJQUFJLENBQUMsV0FBdUM7UUFDM0Ms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 \ No newline at end of file diff --git a/build/lib/mangle/index.ts b/build/lib/mangle/index.ts index a87fe97f456..035653c6f5b 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -278,13 +278,6 @@ function isNameTakenInFile(node: ts.Node, name: string): boolean { return false; } -const fileIdents = new class { - private readonly idents = new ShortIdent('$'); - - next() { - return this.idents.next(); - } -}; const skippedExportMangledFiles = [ // Build @@ -346,6 +339,7 @@ class DeclarationData { readonly fileName: string, readonly node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.EnumDeclaration | ts.VariableDeclaration, private readonly service: ts.LanguageService, + fileIdents: ShortIdent, ) { // Todo: generate replacement names based on usage count, with more used names getting shorter identifiers this.replacementName = fileIdents.next(); @@ -427,6 +421,8 @@ export class Mangler { // - Find all classes and their field info. // - Find exported symbols. + const fileIdents = new ShortIdent('$'); + const visit = (node: ts.Node): void => { if (this.config.manglePrivateFields) { if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) { @@ -475,7 +471,7 @@ export class Mangler { return; } - this.allExportedSymbols.add(new DeclarationData(node.getSourceFile().fileName, node, this.service)); + this.allExportedSymbols.add(new DeclarationData(node.getSourceFile().fileName, node, this.service, fileIdents)); } } diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 7ff08b74263..ca1f5eab147 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -38,6 +38,8 @@ "--vscode-charts-yellow", "--vscode-chat-requestBackground", "--vscode-chat-requestBorder", + "--vscode-chat-slashCommandBackground", + "--vscode-chat-slashCommandForeground", "--vscode-checkbox-background", "--vscode-checkbox-border", "--vscode-checkbox-foreground", @@ -211,12 +213,14 @@ "--vscode-editorHoverWidget-foreground", "--vscode-editorHoverWidget-highlightForeground", "--vscode-editorHoverWidget-statusBarBackground", + "--vscode-editorIndentGuide-activeBackground", "--vscode-editorIndentGuide-activeBackground1", "--vscode-editorIndentGuide-activeBackground2", "--vscode-editorIndentGuide-activeBackground3", "--vscode-editorIndentGuide-activeBackground4", "--vscode-editorIndentGuide-activeBackground5", "--vscode-editorIndentGuide-activeBackground6", + "--vscode-editorIndentGuide-background", "--vscode-editorIndentGuide-background1", "--vscode-editorIndentGuide-background2", "--vscode-editorIndentGuide-background3", @@ -314,11 +318,11 @@ "--vscode-inlineChat-regionHighlight", "--vscode-inlineChat-shadow", "--vscode-inlineChatDiff-inserted", + "--vscode-inlineChatDiff-removed", "--vscode-inlineChatInput-background", "--vscode-inlineChatInput-border", "--vscode-inlineChatInput-focusBorder", "--vscode-inlineChatInput-placeholderForeground", - "--vscode-inlineChatrDiff-removed", "--vscode-input-background", "--vscode-input-border", "--vscode-input-foreground", @@ -532,6 +536,7 @@ "--vscode-sideBarTitle-foreground", "--vscode-sideBySideEditor-horizontalBorder", "--vscode-sideBySideEditor-verticalBorder", + "--vscode-simpleFindWidget-sashBorder", "--vscode-statusBar-background", "--vscode-statusBar-border", "--vscode-statusBar-debuggingBackground", @@ -700,6 +705,7 @@ "--background-light", "--dropdown-padding-bottom", "--dropdown-padding-top", + "--hover-maxWidth", "--insert-border-color", "--last-tab-margin-right", "--monaco-monospace-font", @@ -739,6 +745,7 @@ "--vscode-repl-line-height", "--vscode-sash-hover-size", "--vscode-sash-size", + "--vscode-editorStickyScroll-scrollableWidth", "--window-border-color", "--workspace-trust-check-color", "--workspace-trust-selected-color", @@ -758,6 +765,7 @@ "--z-index-notebook-progress-bar", "--z-index-notebook-scrollbar", "--z-index-run-button-container", + "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} +} \ No newline at end of file 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsY3VsYXRlLWRlcHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjYWxjdWxhdGUtZGVwcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxpREFBMEM7QUFDMUMsMkJBQXlDO0FBQ3pDLDJCQUE0QjtBQUM1Qiw2QkFBOEI7QUFDOUIsc0RBQXNEO0FBQ3RELDJDQUE2QztBQUc3QyxTQUFnQixtQkFBbUIsQ0FBQyxLQUFlLEVBQUUsSUFBc0IsRUFBRSxPQUFlO0lBQzNGLE1BQU0sWUFBWSxHQUFrQixLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ2pHLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLENBQUMsMEJBQWMsQ0FBQyxDQUFDO0lBQ2xELFlBQVksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNyQyxPQUFPLFlBQVksQ0FBQztBQUNyQixDQUFDO0FBTEQsa0RBS0M7QUFFRCw2SEFBNkg7QUFDN0gsU0FBUyxvQkFBb0IsQ0FBQyxVQUFrQixFQUFFLElBQXNCLEVBQUUsT0FBZTtJQUN4RixJQUFJO1FBQ0gsSUFBSSxDQUFDLENBQUMsSUFBQSxhQUFRLEVBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxHQUFHLGNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRTtZQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsVUFBVSx1Q0FBdUMsQ0FBQyxDQUFDO1NBQzdFO0tBQ0Q7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNYLDhEQUE4RDtRQUM5RCxPQUFPLENBQUMsS0FBSyxDQUFDLGdCQUFnQixHQUFHLFVBQVUsR0FBRyxjQUFjLENBQUMsQ0FBQztLQUM5RDtJQUVELHdDQUF3QztJQUN4QyxNQUFNLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLFlB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 \ 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..bd2ce4fa3d7 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 @@ -28,20 +30,23 @@ exports.referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.14)', + 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.2.5)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.5.12)', - 'libdrm2 (>= 2.4.60)', + 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.16.0)', - 'libglib2.0-0 (>= 2.39.4)', + 'libglib2.0-0 (>= 2.37.3)', + '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 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', 'libsecret-1-0 (>= 0.18)', @@ -64,6 +69,7 @@ exports.referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.15)', + 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.4)', 'libc6 (>= 2.8)', @@ -71,15 +77,17 @@ exports.referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.5.12)', - 'libdrm2 (>= 2.4.60)', + 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', - 'libglib2.0-0 (>= 2.39.4)', + 'libglib2.0-0 (>= 2.37.3)', + '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 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', 'libsecret-1-0 (>= 0.18)', @@ -108,15 +116,17 @@ exports.referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.0.2)', - 'libdrm2 (>= 2.4.60)', + 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', - 'libglib2.0-0 (>= 2.39.4)', + 'libglib2.0-0 (>= 2.37.3)', + '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 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', 'libsecret-1-0 (>= 0.18)', @@ -136,4 +146,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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsV0FBVztDQUNYLENBQUM7QUFFRixvSEFBb0g7QUFDcEgsMENBQTBDO0FBQzFDLDhEQUE4RDtBQUNqRCxRQUFBLGVBQWUsR0FBRztJQUM5QixZQUFZLENBQUMseUVBQXlFO0NBQ3RGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHO0lBQzNDLE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1Qix5QkFBeUI7UUFDekIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtJQUNELE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0lBQ0QsT0FBTyxFQUFFO1FBQ1IsaUJBQWlCO1FBQ2pCLHdCQUF3QjtRQUN4QiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLDJCQUEyQjtRQUMzQixpQkFBaUI7UUFDakIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0NBQ0QsQ0FBQyJ9 \ No newline at end of file diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts index 7f6cd6ca8cc..842eaac27cc 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 @@ -28,20 +30,23 @@ export const referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.14)', + 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.2.5)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.5.12)', - 'libdrm2 (>= 2.4.60)', + 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.16.0)', - 'libglib2.0-0 (>= 2.39.4)', + 'libglib2.0-0 (>= 2.37.3)', + '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 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', 'libsecret-1-0 (>= 0.18)', @@ -64,6 +69,7 @@ export const referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.15)', + 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.4)', 'libc6 (>= 2.8)', @@ -71,15 +77,17 @@ export const referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.5.12)', - 'libdrm2 (>= 2.4.60)', + 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', - 'libglib2.0-0 (>= 2.39.4)', + 'libglib2.0-0 (>= 2.37.3)', + '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 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', 'libsecret-1-0 (>= 0.18)', @@ -108,15 +116,17 @@ export const referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.0.2)', - 'libdrm2 (>= 2.4.60)', + 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.12.0)', - 'libglib2.0-0 (>= 2.39.4)', + 'libglib2.0-0 (>= 2.37.3)', + '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 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', 'libsecret-1-0 (>= 0.18)', diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index 0ea66992dd3..381539dd67a 100644 --- a/build/linux/dependencies-generator.js +++ b/build/linux/dependencies-generator.js @@ -21,7 +21,7 @@ const types_2 = require("./rpm/types"); // The reference dependencies, which one has to update when the new dependencies // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/114.0.5735.199:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts index c0d811253d2..5c4b9d240dc 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -23,7 +23,7 @@ import { isRpmArchString, RpmArchString } from './rpm/types'; // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5359.215:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/114.0.5735.199:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index c836348ef51..5e2749a50be 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)', @@ -73,9 +77,11 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', @@ -126,7 +132,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 +151,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', @@ -155,10 +164,12 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)', 'libnss3.so(NSS_3.12)', 'libnss3.so(NSS_3.12.1)', + 'libnss3.so(NSS_3.13)', 'libnss3.so(NSS_3.2)', 'libnss3.so(NSS_3.22)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)', + 'libnss3.so(NSS_3.30)', 'libnss3.so(NSS_3.4)', 'libnss3.so(NSS_3.5)', 'libnss3.so(NSS_3.9.2)', @@ -219,7 +230,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 +246,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)', @@ -244,9 +258,11 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', @@ -289,4 +305,4 @@ exports.referenceGeneratedDepsByArch = { 'xdg-utils' ] }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLCtHQUErRztBQUMvRywrREFBK0Q7QUFDbEQsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHdCQUF3QjtJQUN4Qiw2QkFBNkI7SUFDN0IsNkJBQTZCO0lBQzdCLGdDQUFnQztJQUNoQyx5QkFBeUI7SUFDekIsdUJBQXVCO0lBQ3ZCLFdBQVcsQ0FBQyxpQkFBaUI7Q0FDN0IsQ0FBQztBQUVXLFFBQUEsNEJBQTRCLEdBQUc7SUFDM0MsUUFBUSxFQUFFO1FBQ1QsaUJBQWlCO1FBQ2pCLCtCQUErQjtRQUMvQiwwQ0FBMEM7UUFDMUMsd0NBQXdDO1FBQ3hDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0Isd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLDBCQUEwQjtR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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLCtHQUErRztBQUMvRywrREFBK0Q7QUFDbEQsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHdCQUF3QjtJQUN4Qiw2QkFBNkI7SUFDN0IsNkJBQTZCO0lBQzdCLGdDQUFnQztJQUNoQyx5QkFBeUI7SUFDekIsdUJBQXVCO0lBQ3ZCLFdBQVcsQ0FBQyxpQkFBaUI7Q0FDN0IsQ0FBQztBQUVXLFFBQUEsNEJBQTRCLEdBQUc7SUFDM0MsUUFBUSxFQUFFO1FBQ1QsaUJBQWlCO1FBQ2pCLCtCQUErQjtRQUMvQiwwQ0FBMEM7UUFDMUMsd0NBQXdDO1FBQ3hDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0Isd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLDBCQUEwQjtR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 \ No newline at end of file diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index c262448c318..4e5f64dc20b 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)', @@ -72,9 +76,11 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', @@ -125,7 +131,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 +150,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', @@ -154,10 +163,12 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)', 'libnss3.so(NSS_3.12)', 'libnss3.so(NSS_3.12.1)', + 'libnss3.so(NSS_3.13)', 'libnss3.so(NSS_3.2)', 'libnss3.so(NSS_3.22)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)', + 'libnss3.so(NSS_3.30)', 'libnss3.so(NSS_3.4)', 'libnss3.so(NSS_3.5)', 'libnss3.so(NSS_3.9.2)', @@ -218,7 +229,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 +245,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)', @@ -243,9 +257,11 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', diff --git a/build/npm/dirs.js b/build/npm/dirs.js index 875390550da..faf3a6577a5 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -41,6 +41,7 @@ const dirs = [ 'extensions/references-view', 'extensions/search-result', 'extensions/simple-browser', + 'extensions/tunnel-forwarding', 'extensions/typescript-language-features', 'extensions/vscode-api-tests', 'extensions/vscode-colorize-tests', 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 10514f566bb..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" @@ -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..b6a0a746780 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -9,12 +9,14 @@ const majorNodeVersion = parseInt(nodeVersion[1]); const minorNodeVersion = parseInt(nodeVersion[2]); const patchNodeVersion = parseInt(nodeVersion[3]); -if (majorNodeVersion < 16 || (majorNodeVersion === 16 && minorNodeVersion < 17)) { - console.error('\033[1;31m*** Please use node.js versions >=16.17.x and <17.\033[0;0m'); - err = true; -} -if (majorNodeVersion >= 17) { - console.warn('\033[1;31m*** Warning: Versions of node.js >= 17 have not been tested.\033[0;0m') +if (!process.env['VSCODE_SKIP_NODE_VERSION_CHECK']) { + if (majorNodeVersion < 18 || (majorNodeVersion === 18 && minorNodeVersion < 15)) { + console.error('\033[1;31m*** Please use node.js versions >=18.15.x and <19.\033[0;0m'); + err = true; + } + if (majorNodeVersion >= 19) { + console.warn('\033[1;31m*** Warning: Versions of node.js >= 19 have not been tested.\033[0;0m') + } } const path = require('path'); @@ -123,7 +125,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 b1b42569425..98d4b6578d1 100644 --- a/build/package.json +++ b/build/package.json @@ -28,7 +28,7 @@ "@types/minimist": "^1.2.1", "@types/mkdirp": "^1.0.1", "@types/mocha": "^9.1.1", - "@types/node": "16.x", + "@types/node": "18.x", "@types/p-limit": "^2.2.0", "@types/pump": "^1.0.1", "@types/rimraf": "^2.0.4", diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index f83ae22dfc2..fb521755690 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -109,7 +109,7 @@ dependencies = [ [[package]] name = "inno_updater" -version = "0.10.0" +version = "0.10.1" dependencies = [ "byteorder", "crc", diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index faf7e7fe6d1..cf3cc9de80b 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "inno_updater" -version = "0.10.0" +version = "0.10.1" authors = ["Microsoft "] build = "build.rs" 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 941ebfe4081..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 66ff004f40b..0b3f151633b 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -546,10 +546,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@16.x": - version "16.18.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc" - integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/p-limit@^2.2.0": version "2.2.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..dfcdf4de6bb 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "513ac8b2c47c7e1ac6b9f4fb5ea98e965cf29b66" + "commitHash": "8bab63006effd27646be3d5c07eb09d3282c9b4d" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "108.0.5359.215" + "version": "114.0.5735.248" }, { "component": { @@ -48,7 +48,7 @@ "git": { "name": "ffmpeg", "repositoryUrl": "https://chromium.googlesource.com/chromium/third_party/ffmpeg", - "commitHash": "b9f01c3c54576330b2cf8918c54d5ee5be8faefe" + "commitHash": "8d21d41d8bec5c0b266ee305d1a708dc5c23b594" } }, "isOnlyProductionDependency": true, @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "6b06e89c7dfccec6792008302af1cee57649445c" + "commitHash": "a94966ffc114e831d4a917a97f9958d3385ab6f7" } }, "isOnlyProductionDependency": true, - "version": "16.17.1" + "version": "18.15.0" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "4ade2a6fb65e4b723feb7c09a5df765e5006b378" + "commitHash": "d219fcc8b61dbeb459e92c90a069076f18a9a12a" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "22.3.14" + "version": "25.4.0" }, { "component": { diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 8e6e477991f..f7c60045c12 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -183,17 +183,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -301,11 +290,8 @@ checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", "serde", - "time 0.1.44", - "wasm-bindgen", "winapi", ] @@ -356,7 +342,6 @@ name = "code-cli" version = "0.1.0" dependencies = [ "async-trait", - "atty", "base64 0.21.2", "bytes", "cfg-if", @@ -517,9 +502,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -536,9 +521,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.78" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +checksum = "e88abab2f5abbe4c56e8f1fb431b784d710b709888f35755a160e62e33fe38e8" dependencies = [ "cc", "cxxbridge-flags", @@ -548,9 +533,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.78" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +checksum = "5c0c11acd0e63bae27dcd2afced407063312771212b7a823b4fd72d633be30fb" dependencies = [ "cc", "codespan-reporting", @@ -558,24 +543,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 1.0.103", + "syn 2.0.18", ] [[package]] name = "cxxbridge-flags" -version = "1.0.78" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" +checksum = "8d3816ed957c008ccd4728485511e3d9aaf7db419aa321e3d2c5a2f3411e36c8" [[package]] name = "cxxbridge-macro" -version = "1.0.78" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +checksum = "a26acccf6f445af85ea056362561a24ef56cdc15fcc685f03aec50b9c702cb6d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.18", ] [[package]] @@ -1069,23 +1054,23 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.51" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ "cxx", "cxx-build", @@ -1234,9 +1219,9 @@ checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ "cc", ] @@ -1996,9 +1981,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "secret-service" @@ -2271,9 +2256,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -2298,17 +2283,6 @@ dependencies = [ "syn 2.0.18", ] -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "time" version = "0.3.21" @@ -2649,12 +2623,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2790,6 +2758,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.36.1" @@ -3100,7 +3077,7 @@ dependencies = [ "crc32fast", "crossbeam-utils", "flate2", - "time 0.3.21", + "time", ] [[package]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d685d9efcbb..a25bee1f2ce 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -29,10 +29,9 @@ rmp-serde = "1.1.1" uuid = { version = "1.3.3", features = ["serde", "v4"] } dirs = "5.0.1" rand = "0.8.5" -atty = "0.2.14" opentelemetry = { version = "0.19.0", features = ["rt-tokio"] } serde_bytes = "0.11.9" -chrono = { version = "0.4.26", features = ["serde"] } +chrono = { version = "0.4.26", features = ["serde", "std", "clock"], default-features = false } gethostname = "0.4.3" libc = "0.2.144" tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "2621784a9ad72aa39500372391332a14bad581a3", default-features = false, features = ["connections"] } @@ -57,7 +56,7 @@ bytes = "1.4.0" tar = "0.4.38" [build-dependencies] -serde = "1.0.163" +serde = { version="1.0.163", features = ["derive"] } serde_json = "1.0.96" [target.'cfg(windows)'.dependencies] 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..16078e34044 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -6,53 +6,146 @@ const FILE_HEADER: &str = "/*---------------------------------------------------------------------------------------------\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 *--------------------------------------------------------------------------------------------*/"; use std::{ + collections::HashMap, env, fs, io, - path::PathBuf, - process::{self, Command}, + path::{Path, PathBuf}, + process::{self}, str::FromStr, }; +use serde::{de::DeserializeOwned, Deserialize}; +use serde_json::Value; + fn main() { let files = enumerate_source_files().expect("expected to enumerate files"); ensure_file_headers(&files).expect("expected to ensure file headers"); apply_build_environment_variables(); } -fn apply_build_environment_variables() { - // only do this for local, debug builds - if env::var("PROFILE").unwrap() != "debug" || env::var("VSCODE_CLI_ALREADY_PREPARED").is_ok() { - return; +fn camel_case_to_constant_case(key: &str) -> String { + let mut output = String::new(); + let mut prev_upper = false; + for c in key.chars() { + if c.is_uppercase() { + if prev_upper { + output.push(c.to_ascii_lowercase()); + } else { + output.push('_'); + output.push(c.to_ascii_uppercase()); + } + prev_upper = true; + } else if c.is_lowercase() { + output.push(c.to_ascii_uppercase()); + prev_upper = false; + } else { + output.push(c); + prev_upper = false; + } } - let pkg_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let mut cmd = Command::new("node"); - cmd.arg("../build/azure-pipelines/cli/prepare.js"); - cmd.current_dir(&pkg_dir); - cmd.env("VSCODE_CLI_PREPARE_OUTPUT", "json"); + output +} - let mut distro_location = PathBuf::from_str(&pkg_dir).unwrap(); - distro_location.pop(); // vscode dir - distro_location.pop(); // parent dir - distro_location.push("vscode-distro"); // distro dir, perhaps? - if distro_location.exists() { - cmd.env("VSCODE_CLI_PREPARE_ROOT", distro_location); - cmd.env("VSCODE_QUALITY", "insider"); +fn set_env_vars_from_map_keys(prefix: &str, map: impl IntoIterator) { + let mut win32_app_ids = vec![]; + + for (key, value) in map { + //#region special handling + let value = match key.as_str() { + "tunnelServerQualities" | "serverLicense" => { + Value::String(serde_json::to_string(&value).unwrap()) + } + "nameLong" => { + if let Value::String(s) = &value { + let idx = s.find(" - "); + println!( + "cargo:rustc-env=VSCODE_CLI_QUALITYLESS_PRODUCT_NAME={}", + idx.map(|i| &s[..i]).unwrap_or(s) + ); + } + + value + } + "tunnelApplicationConfig" => { + if let Value::Object(v) = value { + set_env_vars_from_map_keys(&format!("{}_{}", prefix, "TUNNEL"), v); + } + continue; + } + _ => value, + }; + if key.contains("win32") && key.contains("AppId") { + if let Value::String(s) = value { + win32_app_ids.push(s); + continue; + } + } + //#endregion + + if let Value::String(s) = value { + println!( + "cargo:rustc-env={}_{}={}", + prefix, + camel_case_to_constant_case(&key), + s + ); + } } - let output = cmd.output().expect("expected to run prepare script"); - if !output.status.success() { - eprint!( - "error running prepare script: {}", - String::from_utf8_lossy(&output.stderr) + if !win32_app_ids.is_empty() { + println!( + "cargo:rustc-env=VSCODE_CLI_WIN32_APP_IDS={}", + win32_app_ids.join(",") ); - process::exit(output.status.code().unwrap_or(1)); } +} - let vars = serde_json::from_slice::>(&output.stdout) - .expect("expected to deserialize output"); - for (key, value) in vars { - println!("cargo:rustc-env={}={}", key, value); - } +fn read_json_from_path(path: &Path) -> T +where + T: DeserializeOwned, +{ + let mut file = fs::File::open(path).expect("failed to open file"); + serde_json::from_reader(&mut file).expect("failed to deserialize JSON") +} + +fn apply_build_from_product_json(path: &Path) { + let json: HashMap = read_json_from_path(path); + set_env_vars_from_map_keys("VSCODE_CLI", json); +} + +#[derive(Deserialize)] +struct PackageJson { + pub version: String, +} + +fn apply_build_environment_variables() { + let repo_dir = env::current_dir().unwrap().join(".."); + let package_json = read_json_from_path::(&repo_dir.join("package.json")); + println!( + "cargo:rustc-env=VSCODE_CLI_VERSION={}", + package_json.version + ); + + match env::var("VSCODE_CLI_PRODUCT_JSON") { + Ok(v) => { + let path = if cfg!(windows) { + PathBuf::from_str(&v.replace('/', "\\")).unwrap() + } else { + PathBuf::from_str(&v).unwrap() + }; + println!("cargo:warning=loading product.json from <{:?}>", path); + apply_build_from_product_json(&path); + } + + Err(_) => { + apply_build_from_product_json(&repo_dir.join("product.json")); + + let overrides = repo_dir.join("product.overrides.json"); + if overrides.exists() { + apply_build_from_product_json(&overrides); + } + } + }; } fn ensure_file_headers(files: &[PathBuf]) -> Result<(), io::Error> { 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 bc2737afc00..42bd3a894dc 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -161,6 +161,7 @@ impl StoredCredential { struct StorageWithLastRead { storage: Box, + fallback_storage: Option, last_read: Cell, WrappedError>>, } @@ -392,14 +393,18 @@ 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").into()), - _ => 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(e) => { @@ -410,6 +415,7 @@ impl Auth { .read() .map_err(|e| wrap(e, "could not read from file storage")), ), + fallback_storage: None, storage: Box::new(file_storage), } } @@ -531,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..9caee09ed64 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)] @@ -291,7 +304,7 @@ pub enum VersionSubcommand { #[derive(Args, Debug, Clone)] pub struct UseVersionArgs { /// The version of the editor you want to use. Can be "stable", "insiders", - /// a version number, or an absolute path to an existing install. + /// or an absolute path to an existing install. #[clap(value_name = "stable | insiders | x.y.z | path")] pub name: String, @@ -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..0cc0b8d264b 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}, + legal, local_forwarding, 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) } @@ -250,12 +326,12 @@ pub async fn kill(ctx: CommandContext) -> Result { #[derive(Serialize)] pub struct StatusOutput { - pub tunnel: Option, + pub tunnel: Option, pub service_installed: bool, } pub async fn status(ctx: CommandContext) -> Result { - let tunnel_status = do_single_rpc_call::<_, protocol::singleton::Status>( + let tunnel = do_single_rpc_call::<_, protocol::singleton::StatusWithTunnelName>( &ctx.paths.tunnel_lockfile(), ctx.log.clone(), protocol::singleton::METHOD_STATUS, @@ -271,8 +347,8 @@ pub async fn status(ctx: CommandContext) -> Result { ctx.log.result( serde_json::to_string(&StatusOutput { service_installed, - tunnel: match tunnel_status { - Ok(s) => Some(s.tunnel), + tunnel: match tunnel { + Ok(s) => Some(s), Err(CodeError::NoRunningTunnel) => None, Err(e) => return Err(e.into()), }, @@ -327,6 +403,85 @@ 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 = local_forwarding::client(local_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()); + 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?; + } + + 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?; + + local_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 +529,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 +558,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/constants.rs b/cli/src/constants.rs index 2dac5d43563..6f604e8876e 100644 --- a/cli/src/constants.rs +++ b/cli/src/constants.rs @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -use std::collections::HashMap; +use serde::Deserialize; +use std::{collections::HashMap, io::IsTerminal}; use const_format::concatcp; use lazy_static::lazy_static; @@ -32,17 +33,16 @@ pub const VSCODE_CLI_AI_ENDPOINT: Option<&'static str> = option_env!("VSCODE_CLI pub const VSCODE_CLI_QUALITY: Option<&'static str> = option_env!("VSCODE_CLI_QUALITY"); pub const DOCUMENTATION_URL: Option<&'static str> = option_env!("VSCODE_CLI_DOCUMENTATION_URL"); pub const VSCODE_CLI_COMMIT: Option<&'static str> = option_env!("VSCODE_CLI_COMMIT"); -pub const VSCODE_CLI_UPDATE_ENDPOINT: Option<&'static str> = - option_env!("VSCODE_CLI_UPDATE_ENDPOINT"); +pub const VSCODE_CLI_UPDATE_ENDPOINT: Option<&'static str> = option_env!("VSCODE_CLI_UPDATE_URL"); /// Windows lock name for the running tunnel service. Used by the setup script /// to detect a tunnel process. See #179265. pub const TUNNEL_SERVICE_LOCK_NAME: Option<&'static str> = - option_env!("VSCODE_CLI_TUNNEL_SERVICE_MUTEX"); + option_env!("VSCODE_CLI_WIN32_TUNNEL_SERVICE_MUTEX"); /// Windows lock name for the running tunnel without a service. Used by the setup /// script to detect a tunnel process. See #179265. -pub const TUNNEL_CLI_LOCK_NAME: Option<&'static str> = option_env!("VSCODE_CLI_TUNNEL_CLI_MUTEX"); +pub const TUNNEL_CLI_LOCK_NAME: Option<&'static str> = option_env!("VSCODE_CLI_WIN32_TUNNEL_MUTEX"); pub const TUNNEL_SERVICE_USER_AGENT_ENV_VAR: &str = "TUNNEL_SERVICE_USER_AGENT"; @@ -68,16 +68,24 @@ pub const QUALITYLESS_PRODUCT_NAME: &str = match option_env!("VSCODE_CLI_QUALITY /// Name of the application without quality information. pub const QUALITYLESS_SERVER_NAME: &str = concatcp!(QUALITYLESS_PRODUCT_NAME, " Server"); +pub const QUALITY: &str = match VSCODE_CLI_QUALITY { + Some(q) => q, + _ => "oss", +}; + /// Web URL the editor is hosted at. For VS Code, this is vscode.dev. -pub const EDITOR_WEB_URL: Option<&'static str> = option_env!("VSCODE_CLI_EDITOR_WEB_URL"); +pub const EDITOR_WEB_URL: Option<&'static str> = option_env!("VSCODE_CLI_TUNNEL_EDITOR_WEB_URL"); /// Name shown in places where we need to tell a user what a process is, e.g. in sleep inhibition. pub const TUNNEL_ACTIVITY_NAME: &str = concatcp!(PRODUCT_NAME_LONG, " Tunnel"); +/// Download URL of the desktop product. +pub const PRODUCT_DOWNLOAD_URL: Option<&'static str> = option_env!("VSCODE_CLI_DOWNLOAD_URL"); + const NONINTERACTIVE_VAR: &str = "VSCODE_CLI_NONINTERACTIVE"; /// Default data CLI data directory. -pub const DEFAULT_DATA_PARENT_DIR: &str = match option_env!("VSCODE_CLI_DEFAULT_PARENT_DATA_DIR") { +pub const DEFAULT_DATA_PARENT_DIR: &str = match option_env!("VSCODE_CLI_DATA_FOLDER_NAME") { Some(n) => n, None => ".vscode-oss", }; @@ -91,6 +99,12 @@ pub fn get_default_user_agent() -> String { const NO_COLOR_ENV: &str = "NO_COLOR"; +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ServerQualityInfo { + pub server_application_name: String, +} + lazy_static! { pub static ref TUNNEL_SERVICE_USER_AGENT: String = match std::env::var(TUNNEL_SERVICE_USER_AGENT_ENV_VAR) { @@ -98,32 +112,20 @@ lazy_static! { _ => get_default_user_agent(), }; - /// Map of quality names to arrays of app IDs used for them, for example, `{"stable":["ABC123"]}` - pub static ref WIN32_APP_IDS: Option>> = - option_env!("VSCODE_CLI_WIN32_APP_IDS").and_then(|s| serde_json::from_str(s).unwrap()); - - /// Map of quality names to desktop download URIs - pub static ref QUALITY_DOWNLOAD_URIS: Option> = - option_env!("VSCODE_CLI_QUALITY_DOWNLOAD_URIS").and_then(|s| serde_json::from_str(s).unwrap()); - - /// Map of qualities to the long name of the app in that quality - pub static ref PRODUCT_NAME_LONG_MAP: Option> = - option_env!("VSCODE_CLI_NAME_LONG_MAP").and_then(|s| serde_json::from_str(s).unwrap()); - - /// Map of qualities to the application name - pub static ref APPLICATION_NAME_MAP: Option> = - option_env!("VSCODE_CLI_APPLICATION_NAME_MAP").and_then(|s| serde_json::from_str(s).unwrap()); - /// Map of qualities to the server name - pub static ref SERVER_NAME_MAP: Option> = - option_env!("VSCODE_CLI_SERVER_NAME_MAP").and_then(|s| serde_json::from_str(s).unwrap()); + pub static ref SERVER_NAME_MAP: Option> = + option_env!("VSCODE_CLI_TUNNEL_SERVER_QUALITIES").and_then(|s| serde_json::from_str(s).unwrap()); /// Whether i/o interactions are allowed in the current CLI. - pub static ref IS_A_TTY: bool = atty::is(atty::Stream::Stdin); + pub static ref IS_A_TTY: bool = std::io::stdin().is_terminal(); /// Whether i/o interactions are allowed in the current CLI. pub static ref COLORS_ENABLED: bool = *IS_A_TTY && std::env::var(NO_COLOR_ENV).is_err(); /// Whether i/o interactions are allowed in the current CLI. pub static ref IS_INTERACTIVE_CLI: bool = *IS_A_TTY && std::env::var(NONINTERACTIVE_VAR).is_err(); + + /// Map of quality names to arrays of app IDs used for them, for example, `{"stable":["ABC123"]}` + pub static ref WIN32_APP_IDS: Option> = + option_env!("VSCODE_CLI_WIN32_APP_IDS").map(|s| s.split(',').map(|s| s.to_string()).collect()); } diff --git a/cli/src/desktop/version_manager.rs b/cli/src/desktop/version_manager.rs index e08b42aa982..bb0f1011ab6 100644 --- a/cli/src/desktop/version_manager.rs +++ b/cli/src/desktop/version_manager.rs @@ -14,9 +14,8 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use crate::{ - constants::{QUALITYLESS_PRODUCT_NAME, QUALITY_DOWNLOAD_URIS}, + constants::{PRODUCT_DOWNLOAD_URL, QUALITY, QUALITYLESS_PRODUCT_NAME}, log, - options::{self, Quality}, state::{LauncherPaths, PersistedState}, update_service::Platform, util::errors::{AnyError, InvalidRequestedVersion}, @@ -26,34 +25,23 @@ use crate::{ #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(tag = "t", content = "c")] pub enum RequestedVersion { - Quality(options::Quality), - Version { - version: String, - quality: options::Quality, - }, - Commit { - commit: String, - quality: options::Quality, - }, + Default, + Commit(String), Path(String), } lazy_static! { - static ref SEMVER_RE: Regex = Regex::new(r"^\d+\.\d+\.\d+(-insider)?$").unwrap(); - static ref COMMIT_RE: Regex = Regex::new(r"^[a-z]+/[a-e0-f]{40}$").unwrap(); + static ref COMMIT_RE: Regex = Regex::new(r"^[a-e0-f]{40}$").unwrap(); } impl RequestedVersion { pub fn get_command(&self) -> String { match self { - RequestedVersion::Quality(quality) => { - format!("code version use {}", quality.get_machine_name()) + RequestedVersion::Default => { + format!("code version use {}", QUALITY) } - RequestedVersion::Version { version, .. } => { - format!("code version use {}", version) - } - RequestedVersion::Commit { commit, quality } => { - format!("code version use {}/{}", quality.get_machine_name(), commit) + RequestedVersion::Commit(commit) => { + format!("code version use {}/{}", QUALITY, commit) } RequestedVersion::Path(path) => { format!("code version use {}", path) @@ -65,12 +53,11 @@ impl RequestedVersion { impl std::fmt::Display for RequestedVersion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - RequestedVersion::Quality(quality) => write!(f, "{}", quality.get_capitalized_name()), - RequestedVersion::Version { version, .. } => { - write!(f, "{}", version) + RequestedVersion::Default => { + write!(f, "{}", QUALITY) } - RequestedVersion::Commit { commit, quality } => { - write!(f, "{}/{}", quality, commit) + RequestedVersion::Commit(commit) => { + write!(f, "{}/{}", QUALITY, commit) } RequestedVersion::Path(path) => write!(f, "{}", path), } @@ -81,19 +68,8 @@ impl TryFrom<&str> for RequestedVersion { type Error = InvalidRequestedVersion; fn try_from(s: &str) -> Result { - if let Ok(quality) = options::Quality::try_from(s) { - return Ok(RequestedVersion::Quality(quality)); - } - - if SEMVER_RE.is_match(s) { - return Ok(RequestedVersion::Version { - quality: if s.ends_with("-insider") { - options::Quality::Insiders - } else { - options::Quality::Stable - }, - version: s.to_string(), - }); + if s == QUALITY { + return Ok(RequestedVersion::Default); } if Path::is_absolute(&PathBuf::from(s)) { @@ -101,13 +77,7 @@ impl TryFrom<&str> for RequestedVersion { } if COMMIT_RE.is_match(s) { - let idx = s.find('/').expect("expected a /"); - if let Ok(quality) = options::Quality::try_from(&s[0..idx]) { - return Ok(RequestedVersion::Commit { - commit: s[idx + 1..].to_string(), - quality, - }); - } + return Ok(RequestedVersion::Commit(s.to_string())); } Err(InvalidRequestedVersion()) @@ -206,7 +176,7 @@ impl CodeVersionManager { .versions .get(stored.current) .map(|(v, _)| v.clone()) - .unwrap_or(RequestedVersion::Quality(options::Quality::Stable)) + .unwrap_or(RequestedVersion::Default) } /// Tries to get the entrypoint for the version, if one can be found. @@ -221,7 +191,7 @@ impl CodeVersionManager { // For simple quality requests, see if that's installed already on the system let candidates = match &version { - RequestedVersion::Quality(q) => match detect_installed_program(&self.log, *q) { + RequestedVersion::Default => match detect_installed_program(&self.log) { Ok(p) => p, Err(e) => { warning!(self.log, "error looking up installed applications: {}", e); @@ -254,8 +224,8 @@ pub fn prompt_to_install(version: &RequestedVersion) { QUALITYLESS_PRODUCT_NAME, version ); - if let RequestedVersion::Quality(quality) = version { - if let Some(uri) = QUALITY_DOWNLOAD_URIS.as_ref().and_then(|m| m.get(quality)) { + if let RequestedVersion::Default = version { + if let Some(uri) = PRODUCT_DOWNLOAD_URL { // todo: on some platforms, we may be able to help automate installation. For example, // we can unzip the app ourselves on macOS and on windows we can download and spawn the GUI installer #[cfg(target_os = "linux")] @@ -272,11 +242,12 @@ pub fn prompt_to_install(version: &RequestedVersion) { } #[cfg(target_os = "macos")] -fn detect_installed_program(log: &log::Logger, quality: Quality) -> io::Result> { +fn detect_installed_program(log: &log::Logger) -> io::Result> { + use crate::constants::PRODUCT_NAME_LONG; + // easy, fast detection for where apps are usually installed let mut probable = PathBuf::from("/Applications"); - let app_name = quality.get_long_name(); - probable.push(format!("{}.app", app_name)); + probable.push(format!("{}.app", PRODUCT_NAME_LONG)); if probable.exists() { probable.extend(["Contents/Resources", "app", "bin", "code"]); return Ok(vec![probable]); @@ -316,7 +287,7 @@ fn detect_installed_program(log: &log::Logger, quality: Quality) -> io::Result { - if line.starts_with(app_name) && line.ends_with(':') { + if line.starts_with(PRODUCT_NAME_LONG) && line.ends_with(':') { state = State::LookingForLocation; } } @@ -341,13 +312,13 @@ fn detect_installed_program(log: &log::Logger, quality: Quality) -> io::Result io::Result> { - use crate::constants::WIN32_APP_IDS; +fn detect_installed_program(_log: &log::Logger) -> io::Result> { + use crate::constants::{APPLICATION_NAME, WIN32_APP_IDS}; use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}; use winreg::RegKey; let mut output: Vec = vec![]; - let app_ids = match WIN32_APP_IDS.as_ref().and_then(|m| m.get(&quality)) { + let app_ids = match WIN32_APP_IDS.as_ref() { Some(ids) => ids, None => return Ok(output), }; @@ -381,7 +352,7 @@ fn detect_installed_program(_log: &log::Logger, quality: Quality) -> io::Result< [ location.as_str(), "bin", - &format!("{}.cmd", quality.get_application_name()), + &format!("{}.cmd", APPLICATION_NAME), ] .iter() .collect(), @@ -397,7 +368,9 @@ fn detect_installed_program(_log: &log::Logger, quality: Quality) -> io::Result< // Looks for the given binary name in the PATH, returning all candidate matches. // Based on https://github.dev/microsoft/vscode-js-debug/blob/7594d05518df6700df51771895fcad0ddc7f92f9/src/common/pathUtils.ts#L15 #[cfg(target_os = "linux")] -fn detect_installed_program(log: &log::Logger, quality: Quality) -> io::Result> { +fn detect_installed_program(log: &log::Logger) -> io::Result> { + use crate::constants::APPLICATION_NAME; + let path = match std::env::var("PATH") { Ok(p) => p, Err(e) => { @@ -406,11 +379,10 @@ fn detect_installed_program(log: &log::Logger, quality: Quality) -> io::Result continue, Ok(_) => {} @@ -471,97 +443,40 @@ mod tests { fn test_detect_installed_program() { // developers can run this test and debug output manually; VS Code will not // be installed in CI, so the test only makes sure it doesn't error out - let result = detect_installed_program(&log::Logger::test(), Quality::Insiders); + let result = detect_installed_program(&log::Logger::test()); println!("result: {:?}", result); assert!(result.is_ok()); } - #[test] - fn test_requested_version_parses() { - assert_eq!( - RequestedVersion::try_from("1.2.3").unwrap(), - RequestedVersion::Version { - quality: options::Quality::Stable, - version: "1.2.3".to_string(), - } - ); - - assert_eq!( - RequestedVersion::try_from("1.2.3-insider").unwrap(), - RequestedVersion::Version { - quality: options::Quality::Insiders, - version: "1.2.3-insider".to_string(), - } - ); - - assert_eq!( - RequestedVersion::try_from("stable").unwrap(), - RequestedVersion::Quality(options::Quality::Stable) - ); - - assert_eq!( - RequestedVersion::try_from("insiders").unwrap(), - RequestedVersion::Quality(options::Quality::Insiders) - ); - - assert_eq!( - RequestedVersion::try_from("insiders/92fd228156aafeb326b23f6604028d342152313b") - .unwrap(), - RequestedVersion::Commit { - commit: "92fd228156aafeb326b23f6604028d342152313b".to_string(), - quality: options::Quality::Insiders - } - ); - - assert_eq!( - RequestedVersion::try_from("stable/92fd228156aafeb326b23f6604028d342152313b").unwrap(), - RequestedVersion::Commit { - commit: "92fd228156aafeb326b23f6604028d342152313b".to_string(), - quality: options::Quality::Stable - } - ); - - let exe = std::env::current_exe() - .expect("expected to get exe") - .to_string_lossy() - .to_string(); - assert_eq!( - RequestedVersion::try_from(exe.as_str()).unwrap(), - RequestedVersion::Path(exe), - ); - } - #[tokio::test] async fn test_set_preferred_version() { let dir = make_multiple_vscode_install(); let lp = LauncherPaths::new_without_replacements(dir.path().to_owned()); let vm1 = CodeVersionManager::new(log::Logger::test(), &lp, Platform::LinuxARM64); - assert_eq!( - vm1.get_preferred_version(), - RequestedVersion::Quality(options::Quality::Stable) - ); + assert_eq!(vm1.get_preferred_version(), RequestedVersion::Default); vm1.set_preferred_version( - RequestedVersion::Quality(options::Quality::Exploration), + RequestedVersion::Commit("foobar".to_string()), dir.path().join("desktop/stable"), ) .await .expect("expected to store"); vm1.set_preferred_version( - RequestedVersion::Quality(options::Quality::Insiders), + RequestedVersion::Commit("foobar2".to_string()), dir.path().join("desktop/stable"), ) .await .expect("expected to store"); + assert_eq!( vm1.get_preferred_version(), - RequestedVersion::Quality(options::Quality::Insiders) + RequestedVersion::Commit("foobar2".to_string()), ); let vm2 = CodeVersionManager::new(log::Logger::test(), &lp, Platform::LinuxARM64); assert_eq!( vm2.get_preferred_version(), - RequestedVersion::Quality(options::Quality::Insiders) + RequestedVersion::Commit("foobar2".to_string()), ); } 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/options.rs b/cli/src/options.rs index 9423a6da92e..e37a718a49a 100644 --- a/cli/src/options.rs +++ b/cli/src/options.rs @@ -7,7 +7,7 @@ use std::fmt; use serde::{Deserialize, Serialize}; -use crate::constants::{APPLICATION_NAME_MAP, PRODUCT_NAME_LONG_MAP, SERVER_NAME_MAP}; +use crate::constants::SERVER_NAME_MAP; #[derive(clap::ValueEnum, Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum Quality { @@ -38,30 +38,12 @@ impl Quality { } } - /// Product long name - pub fn get_long_name(&self) -> &'static str { - PRODUCT_NAME_LONG_MAP - .as_ref() - .and_then(|m| m.get(self)) - .map(|s| s.as_str()) - .unwrap_or("Code - OSS") - } - - /// Product application name - pub fn get_application_name(&self) -> &'static str { - APPLICATION_NAME_MAP - .as_ref() - .and_then(|m| m.get(self)) - .map(|s| s.as_str()) - .unwrap_or("code") - } - /// Server application name pub fn server_entrypoint(&self) -> String { let mut server_name = SERVER_NAME_MAP .as_ref() .and_then(|m| m.get(self)) - .map(|s| s.as_str()) + .map(|s| s.server_application_name.as_str()) .unwrap_or("code-server-oss") .to_string(); 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..7378cf34afd 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 local_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 1246e1c9441..5bc5e39514a 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -541,6 +541,15 @@ impl<'a> ServerBuilder<'a> { debug!(self.logger, "Starting server with command... {:?}", cmd); + // On Windows spawning a code-server binary will run cmd.exe /c C:\path\to\code-server.cmd... + // This spawns a cmd.exe window for the user, which if they close will kill the code-server process + // and disconnect the tunnel. To prevent this, pass the CREATE_NO_WINDOW flag to the Command + // only on Windows. + // Original issue: https://github.com/microsoft/vscode/issues/184058 + // Partial fix: https://github.com/microsoft/vscode/pull/184621 + #[cfg(target_os = "windows")] + let cmd = cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW); + let child = cmd .stderr(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) @@ -566,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..b77f6da5f2e 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}; @@ -24,15 +23,19 @@ use std::time::Duration; use tokio::sync::{mpsc, watch}; use tunnels::connections::{ForwardedPortConnection, RelayTunnelHost}; use tunnels::contracts::{ - Tunnel, TunnelPort, TunnelRelayTunnelEndpoint, PORT_TOKEN, TUNNEL_PROTOCOL_AUTO, + Tunnel, TunnelAccessControl, TunnelPort, TunnelRelayTunnelEndpoint, PORT_TOKEN, + TUNNEL_ACCESS_SCOPES_CONNECT, TUNNEL_PROTOCOL_AUTO, }; use tunnels::management::{ new_tunnel_management, HttpError, TunnelLocator, TunnelManagementClient, TunnelRequestOptions, NO_REQUEST_OPTIONS, }; +use super::protocol::{self, PortPrivacy}; 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 +137,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 +166,52 @@ impl ActiveTunnel { } /// Forwards a port over TCP. - pub async fn add_port_tcp(&mut self, port_number: u16) -> Result<(), AnyError> { - self.manager.add_port_tcp(port_number).await?; + pub async fn add_port_tcp( + &self, + port_number: u16, + privacy: PortPrivacy, + ) -> Result<(), AnyError> { + self.manager.add_port_tcp(port_number, privacy).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())) + } + + /// Gets an object to read the current tunnel status. + pub fn status(&self) -> StatusLock { + self.manager.get_status() } } 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 +255,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 +268,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 +298,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 +324,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 +337,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 +374,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 +413,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 +443,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 +457,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,64 +506,73 @@ impl DevTunnels { ) -> Result<(PersistedTunnel, Tunnel), AnyError> { info!(self.log, "Creating tunnel with the name: {}", name); - let mut tried_recycle = false; + let tunnel = match self.get_existing_tunnel_with_name(name).await? { + Some(e) => { + let loc = TunnelLocator::try_from(&e).unwrap(); + info!(self.log, "Adopting existing tunnel (ID={:?})", loc); + spanf!( + self.log, + self.log.span("dev-tunnel.tag.get"), + self.client.get_tunnel(&loc, &HOST_TUNNEL_REQUEST_OPTIONS) + ) + .map_err(|e| wrap(e, "failed to lookup tunnel"))? + } + None => { + let new_tunnel = Tunnel { + tags: self.get_tags(name), + ..Default::default() + }; - let new_tunnel = Tunnel { - tags: vec![ - name.to_string(), - PROTOCOL_VERSION_TAG.to_string(), - VSCODE_CLI_TUNNEL_TAG.to_string(), - ], - ..Default::default() - }; + loop { + let result = spanf!( + self.log, + self.log.span("dev-tunnel.create"), + self.client.create_tunnel(&new_tunnel, options) + ); - loop { - let result = spanf!( - self.log, - self.log.span("dev-tunnel.create"), - self.client.create_tunnel(&new_tunnel, options) - ); + match result { + Err(HttpError::ResponseError(e)) + if e.status_code == StatusCode::TOO_MANY_REQUESTS => + { + 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; + } - match result { - Err(HttpError::ResponseError(e)) - if e.status_code == StatusCode::TOO_MANY_REQUESTS => - { - if !tried_recycle && self.try_recycle_tunnel().await? { - tried_recycle = true; - continue; + return Err(AnyError::from(TunnelCreationFailed( + name.to_string(), + detail, + ))); + } + + return Err(AnyError::from(TunnelCreationFailed( + name.to_string(), + "You have exceeded a limit for the port fowarding service. Please remove other machines before trying to add this machine.".to_string(), + ))); + } + Err(e) => { + return Err(AnyError::from(TunnelCreationFailed( + name.to_string(), + format!("{:?}", e), + ))) + } + Ok(t) => break t, } - - if let Some(d) = e.get_details() { - let detail = d.detail.unwrap_or_else(|| "unknown".to_string()); - return Err(AnyError::from(TunnelCreationFailed( - name.to_string(), - detail, - ))); - } - - return Err(AnyError::from(TunnelCreationFailed( - name.to_string(), - "You have exceeded a limit for the port fowarding service. Please remove other machines before trying to add this machine.".to_string(), - ))); - } - Err(e) => { - return Err(AnyError::from(TunnelCreationFailed( - name.to_string(), - format!("{:?}", e), - ))) - } - Ok(t) => { - let pt = PersistedTunnel { - cluster: t.cluster_id.clone().unwrap(), - id: t.tunnel_id.clone().unwrap(), - name: name.to_string(), - }; - - self.launcher_tunnel.save(Some(pt.clone()))?; - return Ok((pt, t)); } } - } + }; + + let pt = PersistedTunnel { + cluster: tunnel.cluster_id.clone().unwrap(), + id: tunnel.tunnel_id.clone().unwrap(), + name: name.to_string(), + }; + + self.launcher_tunnel.save(Some(pt.clone()))?; + Ok((pt, tunnel)) } /// Gets the expected tunnel tags @@ -508,7 +580,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 +594,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 +621,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 +672,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() }) @@ -609,24 +682,28 @@ impl DevTunnels { Ok(tunnels) } - async fn check_is_name_free(&mut self, name: &str) -> Result<(), AnyError> { - let existing = spanf!( + async fn get_existing_tunnel_with_name(&self, name: &str) -> Result, AnyError> { + 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, + limit: 1, + include_ports: true, + token_scopes: vec!["host".to_string()], ..Default::default() }) ) .map_err(|e| wrap(e, "failed to list existing tunnels"))?; - if !existing.is_empty() { - return Err(AnyError::from(TunnelCreationFailed( - name.to_string(), - "tunnel name already in use".to_string(), - ))); - }; - Ok(()) + + Ok(existing.into_iter().next()) + } + + 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( @@ -660,10 +737,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 +779,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 +792,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 @@ -758,10 +848,36 @@ impl DevTunnels { } } +#[derive(Clone, Default)] +pub struct StatusLock(Arc>); + +impl StatusLock { + fn succeed(&self) { + let mut status = self.0.lock().unwrap(); + status.tunnel = protocol::singleton::TunnelState::Connected; + status.last_connected_at = Some(chrono::Utc::now()); + } + + fn fail(&self, reason: String) { + let mut status = self.0.lock().unwrap(); + if let protocol::singleton::TunnelState::Connected = status.tunnel { + status.last_disconnected_at = Some(chrono::Utc::now()); + status.tunnel = protocol::singleton::TunnelState::Disconnected; + } + status.last_fail_reason = Some(reason); + } + + pub fn read(&self) -> protocol::singleton::Status { + let status = self.0.lock().unwrap(); + status.clone() + } +} + struct ActiveTunnelManager { close_tx: Option>, endpoint_rx: watch::Receiver>>, relay: Arc>, + status: StatusLock, } impl ActiveTunnelManager { @@ -777,6 +893,9 @@ impl ActiveTunnelManager { let relay = Arc::new(tokio::sync::Mutex::new(RelayTunnelHost::new(locator, mgmt))); let relay_spawned = relay.clone(); + let status = StatusLock::default(); + + let status_spawned = status.clone(); tokio::spawn(async move { ActiveTunnelManager::spawn_tunnel( log, @@ -784,6 +903,7 @@ impl ActiveTunnelManager { close_rx, endpoint_tx, access_token, + status_spawned, ) .await; }); @@ -792,18 +912,29 @@ impl ActiveTunnelManager { endpoint_rx, relay, close_tx: Some(close_tx), + status, } } + /// Gets a copy of the current tunnel status information + pub fn get_status(&self) -> StatusLock { + self.status.clone() + } + /// Adds a port for TCP/IP forwarding. #[allow(dead_code)] // todo: port forwarding - pub async fn add_port_tcp(&self, port_number: u16) -> Result<(), WrappedError> { + pub async fn add_port_tcp( + &self, + port_number: u16, + privacy: PortPrivacy, + ) -> Result<(), WrappedError> { self.relay .lock() .await .add_port(&TunnelPort { port_number, protocol: Some(TUNNEL_PROTOCOL_AUTO.to_owned()), + access_control: Some(privacy_to_tunnel_acl(privacy)), ..Default::default() }) .await @@ -877,12 +1008,15 @@ impl ActiveTunnelManager { mut close_rx: mpsc::Receiver<()>, endpoint_tx: watch::Sender>>, access_token_provider: impl AccessTokenProvider + 'static, + status: StatusLock, ) { let mut backoff = Backoff::new(Duration::from_secs(5), Duration::from_secs(120)); macro_rules! fail { ($e: expr, $msg: expr) => { - warning!(log, "{}: {}", $msg, $e); + let fmt = format!("{}: {}", $msg, $e); + warning!(log, &fmt); + status.fail(fmt); endpoint_tx.send(Some(Err($e))).ok(); backoff.delay().await; }; @@ -918,6 +1052,7 @@ impl ActiveTunnelManager { }; backoff.reset(); + status.succeed(); endpoint_tx.send(Some(Ok(handle.endpoint().clone()))).ok(); tokio::select! { @@ -998,7 +1133,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; } @@ -1012,6 +1147,26 @@ fn vec_eq_unsorted(a: &[String], b: &[String]) -> bool { true } +fn privacy_to_tunnel_acl(privacy: PortPrivacy) -> TunnelAccessControl { + let mut acl = TunnelAccessControl { entries: vec![] }; + + if privacy == PortPrivacy::Public { + acl.entries + .push(tunnels::contracts::TunnelAccessControlEntry { + kind: tunnels::contracts::TunnelAccessControlEntryType::Anonymous, + provider: None, + is_inherited: false, + is_deny: false, + is_inverse: false, + organization: None, + subjects: vec![], + scopes: vec![TUNNEL_ACCESS_SCOPES_CONNECT.to_string()], + }); + } + + acl +} + #[cfg(test)] mod test { use super::*; diff --git a/cli/src/tunnels/legal.rs b/cli/src/tunnels/legal.rs index 84b72bf8e69..676ccb7da55 100644 --- a/cli/src/tunnels/legal.rs +++ b/cli/src/tunnels/legal.rs @@ -6,9 +6,14 @@ use crate::constants::{IS_INTERACTIVE_CLI, PRODUCT_NAME_LONG}; use crate::state::{LauncherPaths, PersistedState}; use crate::util::errors::{AnyError, MissingLegalConsent}; use crate::util::input::prompt_yn; +use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -const LICENSE_TEXT: Option<&'static str> = option_env!("VSCODE_CLI_REMOTE_LICENSE_TEXT"); +lazy_static! { + static ref LICENSE_TEXT: Option> = + option_env!("VSCODE_CLI_SERVER_LICENSE").and_then(|s| serde_json::from_str(s).unwrap()); +} + const LICENSE_PROMPT: Option<&'static str> = option_env!("VSCODE_CLI_REMOTE_LICENSE_PROMPT"); #[derive(Clone, Default, Serialize, Deserialize)] @@ -20,8 +25,8 @@ pub fn require_consent( paths: &LauncherPaths, accept_server_license_terms: bool, ) -> Result<(), AnyError> { - match LICENSE_TEXT { - Some(t) => println!("{}", t.replace("\\n", "\r\n")), + match &*LICENSE_TEXT { + Some(t) => println!("{}", t.join("\r\n")), None => return Ok(()), } diff --git a/cli/src/tunnels/local_forwarding.rs b/cli/src/tunnels/local_forwarding.rs new file mode 100644 index 00000000000..e6410860cb0 --- /dev/null +++ b/cli/src/tunnels/local_forwarding.rs @@ -0,0 +1,331 @@ +/*--------------------------------------------------------------------------------------------- + * 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, + ops::{Index, IndexMut}, + 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}, + PortPrivacy, + }, + shutdown_signal::ShutdownSignal, +}; + +#[derive(Default, Clone)] +struct PortCount { + public: u32, + private: u32, +} + +impl Index for PortCount { + type Output = u32; + + fn index(&self, privacy: PortPrivacy) -> &Self::Output { + match privacy { + PortPrivacy::Public => &self.public, + PortPrivacy::Private => &self.private, + } + } +} + +impl IndexMut for PortCount { + fn index_mut(&mut self, privacy: PortPrivacy) -> &mut Self::Output { + match privacy { + PortPrivacy::Public => &mut self.public, + PortPrivacy::Private => &mut self.private, + } + } +} + +impl PortCount { + fn is_empty(&self) -> bool { + self.public == 0 && self.private == 0 + } + + fn primary_privacy(&self) -> PortPrivacy { + if self.public > 0 { + PortPrivacy::Public + } else { + PortPrivacy::Private + } + } +} + +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) { + let n = v.get_mut(&p.number).expect("expected port in map"); + n[p.privacy] -= 1; + if n.is_empty() { + v.remove(&p.number); + } + } + } + + for p in ports.iter() { + if !current.contains(p) { + match v.get_mut(&p.number) { + Some(n) => { + n[p.privacy] += 1; + } + None => { + let mut pc = PortCount::default(); + pc[p.privacy] += 1; + v.insert(p.number, pc); + } + }; + } + } + + 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: PortMap = HashMap::new(); + while self.receiver.changed().await.is_ok() { + let next = self.receiver.borrow().clone(); + + for (port, count) in current.iter() { + let privacy = count.primary_privacy(); + if !matches!(next.get(port), Some(n) if n.primary_privacy() == privacy) { + match tunnel.remove_port(*port).await { + Ok(_) => info!(log, "stopped forwarding port {} at {:?}", *port, privacy), + Err(e) => error!(log, "failed to stop forwarding port {}: {}", port, e), + } + } + } + + for (port, count) in next.iter() { + let privacy = count.primary_privacy(); + if !matches!(current.get(port), Some(n) if n.primary_privacy() == privacy) { + match tunnel.add_port_tcp(*port, privacy).await { + Ok(_) => info!(log, "forwarding port {} at {:?}", port, privacy), + Err(e) => error!(log, "failed to forward port {}: {}", port, 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/port_forwarder.rs b/cli/src/tunnels/port_forwarder.rs index bb60a670dea..093c1c8176f 100644 --- a/cli/src/tunnels/port_forwarder.rs +++ b/cli/src/tunnels/port_forwarder.rs @@ -12,7 +12,7 @@ use crate::{ util::errors::{AnyError, CannotForwardControlPort, ServerHasClosed}, }; -use super::dev_tunnels::ActiveTunnel; +use super::{dev_tunnels::ActiveTunnel, protocol::PortPrivacy}; pub enum PortForwardingRec { Forward(u16, oneshot::Sender>), @@ -87,11 +87,11 @@ impl PortForwardingProcessor { } if !self.forwarded.contains(&port) { - tunnel.add_port_tcp(port).await?; + tunnel.add_port_tcp(port, PortPrivacy::Private).await?; 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..316e3672ba6 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,8 +214,42 @@ pub struct ChallengeVerifyParams { pub response: String, } +#[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)] +#[serde(rename_all = "lowercase")] +pub enum PortPrivacy { + Public, + Private, +} + +pub mod forward_singleton { + use serde::{Deserialize, Serialize}; + + use super::PortPrivacy; + + pub const METHOD_SET_PORTS: &str = "set_ports"; + + #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] + pub struct PortRec { + pub number: u16, + pub privacy: PortPrivacy, + } + + 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 chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; pub const METHOD_RESTART: &str = "restart"; @@ -233,17 +272,41 @@ pub mod singleton { pub message: String, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Clone, Default)] + pub struct StatusWithTunnelName { + pub name: Option, + #[serde(flatten)] + pub status: Status, + } + + #[derive(Serialize, Deserialize, Clone)] pub struct Status { + pub started_at: DateTime, pub tunnel: TunnelState, + pub last_connected_at: Option>, + pub last_disconnected_at: Option>, + pub last_fail_reason: Option, + } + + impl Default for Status { + fn default() -> Self { + Self { + started_at: Utc::now(), + tunnel: TunnelState::Disconnected, + last_connected_at: None, + last_disconnected_at: None, + last_fail_reason: None, + } + } } #[derive(Deserialize, Serialize, Debug)] pub struct LogReplayFinished {} - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, Default, Clone)] pub enum TunnelState { + #[default] Disconnected, - Connected { name: String }, + Connected, } } 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_client.rs b/cli/src/tunnels/singleton_client.rs index ef9fdf85cc0..ca29b7349f1 100644 --- a/cli/src/tunnels/singleton_client.rs +++ b/cli/src/tunnels/singleton_client.rs @@ -114,16 +114,18 @@ pub async fn start_singleton_client(args: SingletonClientArgs) -> bool { CONTROL_INSTRUCTIONS_COMMON }); - let res = c.caller.call::<_, _, protocol::singleton::Status>( - protocol::singleton::METHOD_STATUS, - protocol::EmptyObject {}, - ); + let res = c + .caller + .call::<_, _, protocol::singleton::StatusWithTunnelName>( + protocol::singleton::METHOD_STATUS, + protocol::EmptyObject {}, + ); // we want to ensure the "listening" string always gets printed for // consumers (i.e. VS Code). Ask for it. If the tunnel is not currently // connected though, it will be soon, and that'll be in the log replays. if let Ok(Ok(s)) = res.await { - if let protocol::singleton::TunnelState::Connected { name } = s.tunnel { + if let Some(name) = s.name { print_listening(&c.log, &name); } } diff --git a/cli/src/tunnels/singleton_server.rs b/cli/src/tunnels/singleton_server.rs index 703c09f5120..6189c06a493 100644 --- a/cli/src/tunnels/singleton_server.rs +++ b/cli/src/tunnels/singleton_server.rs @@ -11,7 +11,7 @@ use std::{ use super::{ code_server::CodeServerArgs, control_server::ServerTermination, - dev_tunnels::ActiveTunnel, + dev_tunnels::{ActiveTunnel, StatusLock}, protocol, shutdown_signal::{ShutdownRequest, ShutdownSignal}, }; @@ -48,18 +48,28 @@ pub struct SingletonServerArgs<'a> { pub log_broadcast: &'a BroadcastLogSink, } +struct StatusInfo { + name: String, + lock: StatusLock, +} + #[derive(Clone)] struct SingletonServerContext { log: log::Logger, shutdown_tx: broadcast::Sender, broadcast_tx: broadcast::Sender>, - current_name: Arc>>, + // ugly: a lock in a lock. current_status needs to be provided only + // after we set up the tunnel, however the tunnel is created after the + // singleton server starts to avoid a gap in singleton availability. + // However, this should be safe, as the lock is only used for immediate + // data reads (in the `status` method). + current_status: Arc>>, } pub struct RpcServer { fut: JoinHandle>, shutdown_broadcast: broadcast::Sender, - current_name: Arc>>, + current_status: Arc>>, } pub fn make_singleton_server( @@ -71,12 +81,12 @@ pub fn make_singleton_server( let (shutdown_broadcast, _) = broadcast::channel(4); let rpc = new_json_rpc(); - let current_name = Arc::new(Mutex::new(None)); + let current_status = Arc::new(Mutex::default()); let mut rpc = rpc.methods(SingletonServerContext { log: log.clone(), shutdown_tx: shutdown_broadcast.clone(), broadcast_tx: log_broadcast.get_brocaster(), - current_name: current_name.clone(), + current_status: current_status.clone(), }); rpc.register_sync( @@ -91,12 +101,15 @@ pub fn make_singleton_server( rpc.register_sync( protocol::singleton::METHOD_STATUS, |_: protocol::EmptyObject, c| { - Ok(protocol::singleton::Status { - tunnel: match c.current_name.lock().unwrap().clone() { - Some(name) => protocol::singleton::TunnelState::Connected { name }, - None => protocol::singleton::TunnelState::Disconnected, - }, - }) + Ok(c.current_status + .lock() + .unwrap() + .as_ref() + .map(|s| protocol::singleton::StatusWithTunnelName { + name: Some(s.name.clone()), + status: s.lock.read(), + }) + .unwrap_or_default()) }, ); @@ -124,7 +137,7 @@ pub fn make_singleton_server( }); RpcServer { shutdown_broadcast, - current_name, + current_status, fut, } } @@ -139,8 +152,11 @@ pub async fn start_singleton_server<'a>( { print_listening(&args.log, &args.tunnel.name); - let mut name = args.server.current_name.lock().unwrap(); - *name = Some(args.tunnel.name.clone()) + let mut status = args.server.current_status.lock().unwrap(); + *status = Some(StatusInfo { + name: args.tunnel.name.clone(), + lock: args.tunnel.status(), + }) } let serve_fut = super::serve( @@ -217,7 +233,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 ca6d4bf3d8a..c82e14acc8b 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - use crate::{ +use crate::{ constants::{APPLICATION_NAME, CONTROL_PORT, DOCUMENTATION_URL, QUALITYLESS_PRODUCT_NAME}, rpc::ResponseError, }; @@ -509,10 +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..520682059e1 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", @@ -166,7 +161,7 @@ ] }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index 39b630a1bcc..549c578a9ac 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -115,10 +115,10 @@ dependencies: "@octokit/openapi-types" "^14.0.0" -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== before-after-hook@^2.2.0: version "2.2.3" diff --git a/extensions/cpp/build/update-grammars.js b/extensions/cpp/build/update-grammars.js index 983e61488ef..50b31708c6e 100644 --- a/extensions/cpp/build/update-grammars.js +++ b/extensions/cpp/build/update-grammars.js @@ -8,12 +8,14 @@ var updateGrammar = require('vscode-grammar-updater'); async function updateGrammars() { await updateGrammar.update('jeff-hykin/better-c-syntax', 'autogenerated/c.tmLanguage.json', './syntaxes/c.tmLanguage.json', undefined, 'master'); - await updateGrammar.update('jeff-hykin/better-cpp-syntax', 'autogenerated/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json', undefined, 'master'); - await updateGrammar.update('jeff-hykin/better-cpp-syntax', 'autogenerated/cpp.embedded.macro.tmLanguage.json', './syntaxes/cpp.embedded.macro.tmLanguage.json', undefined, 'master'); + + // The license has changed for these two grammar. We have to freeze them as the new license is not compatible with our license. + // await updateGrammar.update('jeff-hykin/better-cpp-syntax', 'autogenerated/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json', undefined, 'master'); + // await updateGrammar.update('jeff-hykin/better-cpp-syntax', 'autogenerated/cpp.embedded.macro.tmLanguage.json', './syntaxes/cpp.embedded.macro.tmLanguage.json', undefined, 'master'); await updateGrammar.update('NVIDIA/cuda-cpp-grammar', 'syntaxes/cuda-cpp.tmLanguage.json', './syntaxes/cuda-cpp.tmLanguage.json', undefined, 'master'); -// `source.c.platform` which is still included by other grammars + // `source.c.platform` which is still included by other grammars await updateGrammar.update('textmate/c.tmbundle', 'Syntaxes/Platform.tmLanguage', './syntaxes/platform.tmLanguage.json'); } diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index 733388a98a1..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": "878aefe73f942ac68dc4a46a0f154661bcb9eff4" + "commitHash": "772323937fedd65c6dc1c8ce6ea41d97415ed7d1" } }, "license": "MIT", diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 27af054bc3b..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/878aefe73f942ac68dc4a46a0f154661bcb9eff4", + "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" }, @@ -2733,6 +2739,14 @@ } ] }, + "default-literal-expression": { + "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/package.json b/extensions/css-language-features/package.json index 2cdaf061ea9..b267163da39 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -998,7 +998,7 @@ "vscode-uri": "^3.0.7" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index bcf7e51ea88..bdb66297271 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@types/mocha": "^9.1.1", - "@types/node": "16.x" + "@types/node": "18.x" }, "scripts": { "compile": "gulp compile-extension:css-language-features-server", diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 75a7ede426f..649160819e3 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/l10n@^0.0.14": version "0.0.14" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index d39fbd0e22f..acd761d8f5b 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== balanced-match@^1.0.0: version "1.0.0" diff --git a/extensions/debug-auto-launch/package.json b/extensions/debug-auto-launch/package.json index 33dfc9b0ce1..0bc095522a0 100644 --- a/extensions/debug-auto-launch/package.json +++ b/extensions/debug-auto-launch/package.json @@ -33,7 +33,7 @@ ] }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "prettier": { "printWidth": 100, diff --git a/extensions/debug-auto-launch/yarn.lock b/extensions/debug-auto-launch/yarn.lock index e724e7fffa3..8a3d10f2b65 100644 --- a/extensions/debug-auto-launch/yarn.lock +++ b/extensions/debug-auto-launch/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index 5bfccfb575e..fdfe45b73b8 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -168,7 +168,7 @@ ] }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/debug-server-ready/yarn.lock b/extensions/debug-server-ready/yarn.lock index e724e7fffa3..8a3d10f2b65 100644 --- a/extensions/debug-server-ready/yarn.lock +++ b/extensions/debug-server-ready/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 1b791b48365..b7291b9552f 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -479,7 +479,7 @@ "deps": "yarn add @vscode/emmet-helper" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index beb3bfa1ef3..17ccacfc94a 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -410,7 +410,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { // }); // }); - test.skip('No expanding when html is excluded in the settings in completion list', async () => { + 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); @@ -469,7 +469,7 @@ suite('Tests for jsx, xml and xsl', () => { }); }); - test.skip('Expand abbreviation with no self closing tags for html', () => { + test('Expand abbreviation with no self closing tags for html', () => { return withRandomFileEditor('img', 'html', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'html' }); diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index f5321aede70..f177cdb5ec8 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -53,10 +53,10 @@ resolved "https://registry.yarnpkg.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz#46cffea119a0a003312a21c2d9b5628cb5fcd442" integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/emmet-helper@^2.8.8": version "2.9.2" diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index f7ed1ccc164..5d6d342783f 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -67,7 +67,7 @@ }, "devDependencies": { "@types/markdown-it": "0.0.2", - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index 1902937e894..ff42c956777 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" integrity sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/node@^6.0.46": version "6.0.78" 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 ceb5a4c1a27..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": [ @@ -314,7 +314,7 @@ }, { "match": "(?!when|and|or\\b)\\b([\\w0-9'`^._]+)", - "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", + "comments": "Here we need the \\w modifier in order to check that the words are allowed", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -1832,4 +1832,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/git-base/package.json b/extensions/git-base/package.json index d9402c85065..dd013f246e8 100644 --- a/extensions/git-base/package.json +++ b/extensions/git-base/package.json @@ -103,7 +103,7 @@ ] }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/git-base/yarn.lock b/extensions/git-base/yarn.lock index cad6a8475b8..8a3d10f2b65 100644 --- a/extensions/git-base/yarn.lock +++ b/extensions/git-base/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.21.tgz#474d7589a30afcf5291f59bd49cca9ad171ffde4" - integrity sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/git/package.json b/extensions/git/package.json index f45447de7f1..d7d241e0aab 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3017,7 +3017,7 @@ "devDependencies": { "@types/byline": "4.2.31", "@types/mocha": "^9.1.1", - "@types/node": "16.x", + "@types/node": "18.x", "@types/picomatch": "2.3.0", "@types/which": "3.0.0" }, 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/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 8eba9cbac03..af40853e27e 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -749,20 +749,26 @@ export class CommandCenter { const ref = selection.repository.HEAD?.upstream?.name; 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) { + const isWeb = env.uiKind === UIKind.Web; + const isRemote = env.remoteName !== undefined; + + if (isWeb || isRemote) { if (ref !== undefined) { target += `&ref=${encodeURIComponent(ref)}`; } - return Uri.parse(target); - } - // 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); + if (isWeb) { + // Launch desktop client if currently in web + return Uri.parse(target); + } + + if (isRemote) { + // 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 + target += `&windowId=_blank`; + return Uri.parse(target); + } } // Otherwise, directly clone 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 1a928aec322..6be6a6b4e43 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -192,10 +192,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/picomatch@2.3.0": version "2.3.0" 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/package.json b/extensions/github-authentication/package.json index 97b728c3a1d..f5e3c95e6dd 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -64,7 +64,7 @@ "vscode-tas-client": "^0.1.47" }, "devDependencies": { - "@types/node": "16.x", + "@types/node": "18.x", "@types/node-fetch": "^2.5.7" }, "repository": { 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/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/flows.ts b/extensions/github-authentication/src/flows.ts index f3f9277bdc1..5bc9d095385 100644 --- a/extensions/github-authentication/src/flows.ts +++ b/extensions/github-authentication/src/flows.ts @@ -201,7 +201,8 @@ const allFlows: IFlow[] = [ supportsGitHubEnterpriseServer: false, supportsHostedGitHubEnterprise: true, supportsRemoteExtensionHost: true, - supportsWebWorkerExtensionHost: 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, diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index c710cbe4f2f..71aa17bd5cc 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -363,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 7ac5cd8c577..0729c4c5077 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -12,6 +12,8 @@ import { crypto } from './node/crypto'; import { fetching } from './node/fetch'; 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. @@ -22,6 +24,7 @@ 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; @@ -78,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 { @@ -144,6 +152,58 @@ export class GitHubServer implements IGitHubServer { throw new Error(userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.'); } + public async logout(session: vscode.AuthenticationSession): Promise { + this._logger.trace(`Deleting session (${session.id}) from server...`); + + 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; + } + + // 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 { + const body = await result.text(); + throw new Error(body); + } catch (e) { + throw new Error(`${result.status} ${result.statusText}`); + } + } catch (e) { + this._logger.warn('Failed to delete token from server.' + e.message ?? e); + } + } + private getServerUri(path: string = '') { const apiUri = this.baseUri; // github.com and Hosted GitHub Enterprise instances 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 752933d621f..858f60ddaff 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -178,10 +178,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b" integrity sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/extension-telemetry@0.7.5": version "0.7.5" diff --git a/extensions/github/package.json b/extensions/github/package.json index 6e655fca983..ec02df2ec85 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -186,7 +186,7 @@ "@vscode/extension-telemetry": "0.7.5" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts index b270792404f..911f0e5376b 100644 --- a/extensions/github/src/links.ts +++ b/extensions/github/src/links.ts @@ -191,6 +191,8 @@ 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 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 323df7dadbe..f562d36c972 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -294,10 +294,10 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/extension-telemetry@0.7.5": version "0.7.5" diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index 35fc30eaba1..0c00aec71c6 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/grunt/yarn.lock b/extensions/grunt/yarn.lock index e724e7fffa3..8a3d10f2b65 100644 --- a/extensions/grunt/yarn.lock +++ b/extensions/grunt/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 10e41222219..3e29c75fe4d 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/gulp/yarn.lock b/extensions/gulp/yarn.lock index e724e7fffa3..8a3d10f2b65 100644 --- a/extensions/gulp/yarn.lock +++ b/extensions/gulp/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 972caba7fb3..58bd75f70e9 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -263,7 +263,7 @@ "vscode-uri": "^3.0.7" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 5c3e9e11623..3304c243b35 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@types/mocha": "^9.1.1", - "@types/node": "16.x" + "@types/node": "18.x" }, "scripts": { "compile": "npx gulp compile-extension:html-language-features-server", 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/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 8bed471b29c..fe6c91e3cf8 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/l10n@^0.0.14": version "0.0.14" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index c6dda3cbc53..8bf14ed8f82 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-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/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/extension-telemetry@^0.7.5": version "0.7.5" diff --git a/extensions/ipynb/src/notebookImagePaste.ts b/extensions/ipynb/src/notebookImagePaste.ts index 5a164643a15..94292c26a74 100644 --- a/extensions/ipynb/src/notebookImagePaste.ts +++ b/extensions/ipynb/src/notebookImagePaste.ts @@ -15,6 +15,7 @@ enum MimeType { png = 'image/png', tiff = 'image/tiff', webp = 'image/webp', + plain = 'text/plain', uriList = 'text/uri-list', } @@ -47,9 +48,7 @@ function getImageMimeType(uri: vscode.Uri): string | undefined { class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider { - private readonly id = 'insertAttachment'; - - private readonly defaultPriority = 5; + public readonly id = 'insertAttachment'; async provideDocumentPasteEdits( document: vscode.TextDocument, @@ -67,8 +66,8 @@ class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscod return; } - const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, this.id, vscode.l10n.t('Insert Image as Attachment')); - pasteEdit.priority = this.getPastePriority(dataTransfer); + const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, vscode.l10n.t('Insert Image as Attachment')); + pasteEdit.yieldTo = [{ mimeType: MimeType.plain }]; pasteEdit.additionalEdit = insert.additionalEdit; return pasteEdit; } @@ -85,23 +84,12 @@ class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscod } const dropEdit = new vscode.DocumentDropEdit(insert.insertText); - dropEdit.id = this.id; - dropEdit.priority = this.defaultPriority; + dropEdit.yieldTo = [{ mimeType: MimeType.plain }]; dropEdit.additionalEdit = insert.additionalEdit; dropEdit.label = vscode.l10n.t('Insert Image as Attachment'); return dropEdit; } - private getPastePriority(dataTransfer: vscode.DataTransfer): number { - if (dataTransfer.get('text/plain')) { - // Deprioritize in favor of normal text content - return -5; - } - - // Otherwise boost priority so attachments are preferred - return this.defaultPriority; - } - private async createInsertImageAttachmentEdit( document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, @@ -311,12 +299,14 @@ export function notebookImagePasteSetup(): vscode.Disposable { const provider = new DropOrPasteEditProvider(); return vscode.Disposable.from( vscode.languages.registerDocumentPasteEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, provider, { + id: provider.id, pasteMimeTypes: [ MimeType.png, MimeType.uriList, ], }), vscode.languages.registerDocumentDropEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, provider, { + id: provider.id, dropMimeTypes: [ ...Object.values(imageExtToMime), MimeType.uriList, diff --git a/extensions/jake/package.json b/extensions/jake/package.json index e34118ca7a3..637d417e503 100644 --- a/extensions/jake/package.json +++ b/extensions/jake/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/jake/yarn.lock b/extensions/jake/yarn.lock index e724e7fffa3..8a3d10f2b65 100644 --- a/extensions/jake/yarn.lock +++ b/extensions/jake/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== 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/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..05075ec1389 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", @@ -163,7 +169,7 @@ "vscode-languageclient": "^8.2.0-next.1" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", 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/server/package.json b/extensions/json-language-features/server/package.json index 72e707a58e6..b0012a72816 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@types/mocha": "^9.1.1", - "@types/node": "16.x" + "@types/node": "18.x" }, "scripts": { "prepublishOnly": "npm run clean && npm run compile", diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index c8fabcbc15c..48f16a0f42f 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/l10n@^0.0.13": version "0.0.13" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 6113220bc30..98083a9489f 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-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/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/extension-telemetry@^0.7.5": version "0.7.5" 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": "(?:(?:(?:(?:(?:(?:(?:(\\$)((? tbody > tr + tr > td { } blockquote { - margin: 0 7px 0 5px; - padding: 0 16px 0 10px; + margin: 0; + padding: 2px 16px 0 10px; border-left-width: 5px; border-left-style: solid; + border-radius: 2px; } code { @@ -232,6 +233,7 @@ pre code { pre { background-color: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-widget-border); } .vscode-high-contrast h1 { diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 0c5e79b6373..60e80b169d3 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -502,7 +502,7 @@ "type": "string", "scope": "resource", "markdownDescription": "%configuration.markdown.editor.pasteUrlAsFormattedLink.enabled%", - "default":"smart", + "default":"never", "enum": [ "always", "smart", @@ -722,7 +722,7 @@ }, "dependencies": { "@vscode/extension-telemetry": "0.7.5", - "dompurify": "^2.4.1", + "dompurify": "^3.0.5", "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.1", @@ -732,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 0468fbf1d79..7336657621e 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -44,9 +44,9 @@ "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 create a Markdown link when a URL is pasted into the Markdown editor.", - "configuration.pasteUrlAsFormattedLink.smart": "Does not create a Markdown link within a link snippet or code bracket.", - "configuration.pasteUrlAsFormattedLink.never": "Never create a Markdown link when a URL is pasted into the Markdown editor.", + "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/server/package.json b/extensions/markdown-language-features/server/package.json index 87ddad6aef1..1b3e549b748 100644 --- a/extensions/markdown-language-features/server/package.json +++ b/extensions/markdown-language-features/server/package.json @@ -22,7 +22,7 @@ "vscode-uri": "^3.0.7" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "scripts": { "compile": "gulp compile-extension:markdown-language-features-server", diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock index 6c6560ec7ea..c2a76d7f391 100644 --- a/extensions/markdown-language-features/server/yarn.lock +++ b/extensions/markdown-language-features/server/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.18.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc" - integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/l10n@^0.0.10": version "0.0.10" diff --git a/extensions/markdown-language-features/src/commands/insertResource.ts b/extensions/markdown-language-features/src/commands/insertResource.ts index da50c83c991..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, smartPaste = false) { +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, smartPaste, { + 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/extension.shared.ts b/extensions/markdown-language-features/src/extension.shared.ts index 79242573136..0d6c160bffe 100644 --- a/extensions/markdown-language-features/src/extension.shared.ts +++ b/extensions/markdown-language-features/src/extension.shared.ts @@ -7,10 +7,10 @@ import * as vscode from 'vscode'; import { MdLanguageClient } from './client/client'; import { CommandManager } from './commandManager'; import { registerMarkdownCommands } from './commands/index'; -import { registerPasteSupport } from './languageFeatures/copyFiles/copyPaste'; -import { registerLinkPasteSupport } from './languageFeatures/copyFiles/copyPasteLinks'; +import { registerPasteSupport } from './languageFeatures/copyFiles/pasteResourceProvider'; +import { registerLinkPasteSupport } from './languageFeatures/copyFiles/pasteUrlProvider'; import { registerDiagnosticSupport } from './languageFeatures/diagnostics'; -import { registerDropIntoEditorSupport } from './languageFeatures/copyFiles/dropIntoEditor'; +import { registerDropIntoEditorSupport } from './languageFeatures/copyFiles/dropResourceProvider'; import { registerFindFileReferenceSupport } from './languageFeatures/fileReferences'; import { registerUpdateLinksOnRename } from './languageFeatures/linkUpdater'; import { ILogger } from './logging'; 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/copyPasteLinks.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts deleted file mode 100644 index 90755d990fc..00000000000 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts +++ /dev/null @@ -1,52 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { getMarkdownLink } from './shared'; -class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { - - 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<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'smart'); - if (enabled === 'never') { - return; - } - - // Check if dataTransfer contains a URL - const item = dataTransfer.get('text/plain'); - try { - new URL(await item?.value); - } catch (error) { - return; - } - - const uriEdit = new vscode.DocumentPasteEdit('', this.id, ''); - const urlList = await item?.asString(); - if (!urlList) { - return undefined; - } - const pasteEdit = await getMarkdownLink(document, ranges, urlList, token); - if (!pasteEdit) { - return; - } - - uriEdit.label = pasteEdit.label; - uriEdit.additionalEdit = pasteEdit.additionalEdits; - return uriEdit; - } -} - -export function registerLinkPasteSupport(selector: vscode.DocumentSelector,) { - return vscode.languages.registerDocumentPasteEditProvider(selector, new PasteLinkEditProvider(), { - pasteMimeTypes: [ - 'text/plain', - ] - }); -} diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts similarity index 68% rename from extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts rename to extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts index 95c8455e307..1c5a19461cf 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts @@ -4,12 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { createEditForMediaFiles as createEditForMediaFiles, mediaMimes, tryGetUriListSnippet } from './shared'; +import { Mime, mediaMimes } from '../../util/mimes'; import { Schemes } from '../../util/schemes'; +import { createEditForMediaFiles, tryGetUriListSnippet } from './shared'; +class ResourceDropProvider implements vscode.DocumentDropEditProvider { -class MarkdownImageDropProvider implements vscode.DocumentDropEditProvider { - private readonly _id = 'insertLink'; + public static readonly id = 'insertLink'; + + public static readonly dropMimeTypes = [ + Mime.textUriList, + ...mediaMimes, + ]; + + private readonly _yieldTo = [ + { mimeType: 'text/plain' }, + { extensionId: 'vscode.ipynb', providerId: 'insertAttachment' }, + ]; async provideDocumentDropEdits(document: vscode.TextDocument, _position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true); @@ -30,18 +41,19 @@ class MarkdownImageDropProvider implements vscode.DocumentDropEditProvider { } private async _getUriListEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { - const urlList = await dataTransfer.get('text/uri-list')?.asString(); - if (!urlList) { + const urlList = await dataTransfer.get(Mime.textUriList)?.asString(); + if (!urlList || token.isCancellationRequested) { return undefined; } + const snippet = await tryGetUriListSnippet(document, urlList, token); if (!snippet) { return undefined; } const edit = new vscode.DocumentDropEdit(snippet.snippet); - edit.id = this._id; edit.label = snippet.label; + edit.yieldTo = this._yieldTo; return edit; } @@ -55,24 +67,19 @@ class MarkdownImageDropProvider implements vscode.DocumentDropEditProvider { return; } - const filesEdit = await createEditForMediaFiles(document, dataTransfer, token); - if (!filesEdit) { + const edit = await createEditForMediaFiles(document, dataTransfer, token); + if (!edit) { return; } - const edit = new vscode.DocumentDropEdit(filesEdit.snippet); - edit.id = this._id; - edit.label = filesEdit.label; - edit.additionalEdit = filesEdit.additionalEdits; - return edit; + const dropEdit = new vscode.DocumentDropEdit(edit.snippet); + dropEdit.label = edit.label; + dropEdit.additionalEdit = edit.additionalEdits; + dropEdit.yieldTo = this._yieldTo; + return dropEdit; } } export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector) { - return vscode.languages.registerDocumentDropEditProvider(selector, new MarkdownImageDropProvider(), { - dropMimeTypes: [ - 'text/uri-list', - ...mediaMimes, - ] - }); + return vscode.languages.registerDocumentDropEditProvider(selector, new ResourceDropProvider(), ResourceDropProvider); } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts similarity index 56% rename from extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts rename to extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts index 24e23758d25..cd051210d2d 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts @@ -4,12 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { Mime, mediaMimes } from '../../util/mimes'; import { Schemes } from '../../util/schemes'; -import { createEditForMediaFiles, getMarkdownLink, mediaMimes } from './shared'; +import { PasteUrlAsFormattedLink, createEditAddingLinksForUriList, createEditForMediaFiles, getPasteUrlAsFormattedLinkSetting } from './shared'; -class PasteEditProvider implements vscode.DocumentPasteEditProvider { +class PasteResourceEditProvider implements vscode.DocumentPasteEditProvider { - private readonly _id = 'insertLink'; + public static readonly id = 'insertLink'; + + public static readonly pasteMimeTypes = [ + Mime.textUriList, + ...mediaMimes, + ]; + + private readonly _yieldTo = [ + { mimeType: 'text/plain' }, + { extensionId: 'vscode.ipynb', providerId: 'insertAttachment' }, + ]; async provideDocumentPasteEdits( document: vscode.TextDocument, @@ -27,19 +38,28 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider { return createEdit; } - const uriEdit = new vscode.DocumentPasteEdit('', this._id, ''); - const urlList = await dataTransfer.get('text/uri-list')?.asString(); - if (!urlList) { + if (token.isCancellationRequested) { return; } - const pasteEdit = await getMarkdownLink(document, ranges, urlList, token); + + return this._getUriListEdit(document, ranges, dataTransfer, token); + } + + private async _getUriListEdit(document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { + const uriList = await dataTransfer.get(Mime.textUriList)?.asString(); + if (!uriList || token.isCancellationRequested) { + return; + } + + const pasteUrlSetting = getPasteUrlAsFormattedLinkSetting(document); + const pasteEdit = await createEditAddingLinksForUriList(document, ranges, uriList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); if (!pasteEdit) { return; } - uriEdit.label = pasteEdit.label; + const uriEdit = new vscode.DocumentPasteEdit('', pasteEdit.label); uriEdit.additionalEdit = pasteEdit.additionalEdits; - uriEdit.priority = this._getPriority(dataTransfer); + uriEdit.yieldTo = this._yieldTo; return uriEdit; } @@ -58,26 +78,13 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider { return; } - const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, this._id, edit.label); + const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, edit.label); pasteEdit.additionalEdit = edit.additionalEdits; - pasteEdit.priority = this._getPriority(dataTransfer); + pasteEdit.yieldTo = this._yieldTo; return pasteEdit; } - - private _getPriority(dataTransfer: vscode.DataTransfer): number { - if (dataTransfer.get('text/plain')) { - // Deprioritize in favor of normal text content - return -10; - } - return 0; - } } export function registerPasteSupport(selector: vscode.DocumentSelector,) { - return vscode.languages.registerDocumentPasteEditProvider(selector, new PasteEditProvider(), { - pasteMimeTypes: [ - 'text/uri-list', - ...mediaMimes, - ] - }); + return vscode.languages.registerDocumentPasteEditProvider(selector, new PasteResourceEditProvider(), PasteResourceEditProvider); } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts new file mode 100644 index 00000000000..b68b9cdaa2b --- /dev/null +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Mime } from '../../util/mimes'; +import { createEditAddingLinksForUriList, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink, validateLink } from './shared'; + +class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { + + public static readonly id = 'insertMarkdownLink'; + + public static readonly pasteMimeTypes = [ + Mime.textPlain, + ]; + + async provideDocumentPasteEdits( + document: vscode.TextDocument, + ranges: readonly vscode.Range[], + dataTransfer: vscode.DataTransfer, + token: vscode.CancellationToken, + ): Promise { + const pasteUrlSetting = getPasteUrlAsFormattedLinkSetting(document); + if (pasteUrlSetting === PasteUrlAsFormattedLink.Never) { + return; + } + + const item = dataTransfer.get(Mime.textPlain); + const urlList = await item?.asString(); + if (token.isCancellationRequested || !urlList || !validateLink(urlList).isValid) { + return; + } + + const pasteEdit = await createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); + if (!pasteEdit) { + return; + } + + const edit = new vscode.DocumentPasteEdit('', pasteEdit.label); + edit.additionalEdit = pasteEdit.additionalEdits; + edit.yieldTo = pasteEdit.markdownLink ? undefined : [{ mimeType: Mime.textPlain }]; + return edit; + } +} + +export function registerLinkPasteSupport(selector: vscode.DocumentSelector,) { + return vscode.languages.registerDocumentPasteEditProvider(selector, new PasteUrlEditProvider(), PasteUrlEditProvider); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index f40fea3556f..ef4ec6b34be 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -6,10 +6,11 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as URI from 'vscode-uri'; -import { Schemes } from '../../util/schemes'; -import { NewFilePathGenerator } from './copyFiles'; import { coalesce } from '../../util/arrays'; import { getDocumentDir } from '../../util/document'; +import { mediaMimes } from '../../util/mimes'; +import { Schemes } from '../../util/schemes'; +import { NewFilePathGenerator } from './copyFiles'; enum MediaKind { Image, @@ -17,9 +18,10 @@ enum MediaKind { Audio, } -const externalUriSchemes = [ +export const externalUriSchemes = [ 'http', 'https', + 'mailto', ]; export const mediaFileExtensions = new Map([ @@ -48,82 +50,129 @@ export const mediaFileExtensions = new Map([ ['wav', MediaKind.Audio], ]); -export const mediaMimes = new Set([ - 'image/bmp', - 'image/gif', - 'image/jpeg', - 'image/png', - 'image/webp', - 'video/mp4', - 'video/ogg', - 'audio/mpeg', - 'audio/aac', - 'audio/x-wav', -]); +const smartPasteRegexes = [ + { regex: /(\[[^\[\]]*](?:\([^\(\)]*\)|\[[^\[\]]*]))/g }, // In a Markdown link + { regex: /^```[\s\S]*?```$/gm }, // In a backtick fenced code block + { regex: /^~~~[\s\S]*?~~~$/gm }, // In a tildefenced code block + { regex: /^\$\$[\s\S]*?\$\$$/gm }, // In a fenced math block + { regex: /`[^`]*`/g }, // In inline code + { regex: /\$[^$]*\$/g }, // 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 function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument): PasteUrlAsFormattedLink { + 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; markdownLink: boolean } | undefined> { -export async function getMarkdownLink(document: vscode.TextDocument, ranges: readonly vscode.Range[], urlList: string, token: vscode.CancellationToken): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> { if (ranges.length === 0) { return; } - const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'always'); - const edits: vscode.SnippetTextEdit[] = []; let placeHolderValue: number = ranges.length; let label: string = ''; - let smartPaste: boolean = false; - for (let i = 0; i < ranges.length; i++) { - if (enabled === 'smart') { - const inMarkdownLink = checkPaste(document, ranges, /\[([^\]]*)\]\(([^)]*)\)/g, i); - const inFencedCode = checkPaste(document, ranges, /^```[\s\S]*?```$/gm, i); - const inFencedMath = checkPaste(document, ranges, /^\$\$[\s\S]*?\$\$$/gm, i); - smartPaste = (inMarkdownLink || inFencedCode || inFencedMath); + let pasteAsMarkdownLink: boolean = true; + let markdownLink: 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); + markdownLink = pasteAsMarkdownLink; // FIX: this will only match the last range } - const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue, smartPaste); + const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(range), placeHolderValue, pasteAsMarkdownLink, isExternalLink); if (!snippet) { return; } - smartPaste = false; + pasteAsMarkdownLink = true; placeHolderValue--; - edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet)); + edits.push(new vscode.SnippetTextEdit(range, snippet.snippet)); label = snippet.label; } const additionalEdits = new vscode.WorkspaceEdit(); additionalEdits.set(document.uri, edits); - return { additionalEdits, label }; + return { additionalEdits, label, markdownLink }; } -function checkPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], regex: RegExp, index: number): boolean { - const rangeStartOffset = document.offsetAt(ranges[index].start); - const rangeEndOffset = document.offsetAt(ranges[index].end); - const matches = [...document.getText().matchAll(regex)]; - for (const match of matches) { - if (match.index !== undefined && rangeStartOffset > match.index && rangeEndOffset < match.index + match[0].length) { - return true; +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 useDefaultPaste = selectedRange.start.character > match.index && selectedRange.end.character < match.index + match[0].length; + if (useDefaultPaste) { + return false; + } + } } } - - return false; + return true; } -export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, smartPaste = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { +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, smartPaste); + return createUriListSnippet(document, uris, uriStrings, title, placeHolderValue, pasteAsMarkdownLink, isExternalLink); } interface UriListSnippetOptions { @@ -141,28 +190,42 @@ interface UriListSnippetOptions { readonly separator?: string; } +export function appendToLinkSnippet( + snippet: vscode.SnippetString, + title: string, + uriString: string, + placeholderValue: number, + isExternalLink: boolean, +): vscode.SnippetString { + snippet.appendText('['); + snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); + snippet.appendText(`](${escapeMarkdownLinkPath(uriString, isExternalLink)})`); + return snippet; +} + export function createUriListSnippet( - document: vscode.TextDocument, + document: SkinnyTextDocument, uris: readonly vscode.Uri[], + uriStrings?: readonly string[], title = '', placeholderValue = 0, - smartPaste = false, + 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; @@ -179,33 +242,26 @@ export function createUriListSnippet( snippet.appendText(`'); - } else { + } else if (insertAsMedia) { if (insertAsMedia) { insertedImageCount++; - 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)})`); - } else { - insertedLinkCount++; - if (smartPaste) { - if (externalUriSchemes.includes(uri.scheme)) { - snippet.appendText(uri.toString(true)); - } else { - 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('['); - snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); - if (externalUriSchemes.includes(uri.scheme)) { - const uriString = uri.toString(true); - snippet.appendText(`](${uriString})`); - } else { - snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); - } + snippet.appendText(escapeMarkdownLinkPath(mdPath, isExternalLink)); } } + } else { + insertedLinkCount++; + if (uriStrings && isExternalLink) { + snippet = appendToLinkSnippet(snippet, title, uriStrings[i], placeholderValue, isExternalLink); + } else { + snippet.appendText(escapeMarkdownLinkPath(mdPath, isExternalLink)); + } } if (i < uris.length - 1 && uris.length > 1) { @@ -325,12 +381,12 @@ 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 { 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/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..f0133ad85cc --- /dev/null +++ b/extensions/markdown-language-features/src/test/markdownLink.test.ts @@ -0,0 +1,233 @@ +/*--------------------------------------------------------------------------------------------- + * 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!'; }, + }; + + 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 snippet with < > when pasted link has an mismatched parentheses', () => { + const uriString = 'https://www.mic(rosoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), 'abc', uriString, 0, true); + assert.strictEqual(snippet?.value, '[${0:abc}]()'); + }); + + test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => { + const uriString = 'https://www.microsoft.com'; + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', 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(''), '', 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(''), '', 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(''), '', 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/src/util/mimes.ts b/extensions/markdown-language-features/src/util/mimes.ts new file mode 100644 index 00000000000..8028294b3f4 --- /dev/null +++ b/extensions/markdown-language-features/src/util/mimes.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const Mime = { + textUriList: 'text/uri-list', + textPlain: 'text/plain', +} as const; + +export const mediaMimes = new Set([ + 'image/bmp', + 'image/gif', + 'image/jpeg', + 'image/png', + 'image/webp', + 'video/mp4', + 'video/ogg', + 'audio/mpeg', + 'audio/aac', + 'audio/x-wav', +]); diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index b6a3e689d99..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" diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index 3c6c41dab7f..00a7136cff2 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -169,7 +169,7 @@ "@vscode/extension-telemetry": "0.7.5" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/merge-conflict/yarn.lock b/extensions/merge-conflict/yarn.lock index d07fe97f099..f07c9a13701 100644 --- a/extensions/merge-conflict/yarn.lock +++ b/extensions/merge-conflict/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/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@vscode/extension-telemetry@0.7.5": version "0.7.5" diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index ecd2573ebf6..c0ed977db77 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -109,7 +109,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "devDependencies": { - "@types/node": "16.x", + "@types/node": "18.x", "@types/node-fetch": "^2.5.7", "@types/randombytes": "^2.0.0", "@types/sha.js": "^2.4.0", diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 187cbf21a48..d7e92e104fb 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -310,9 +310,7 @@ export class AzureActiveDirectoryService { } try { - const session = await this.createSessionWithLocalServer(scopeData); - this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); - return session; + return await this.createSessionWithLocalServer(scopeData); } catch (e) { this._logger.error(`Error creating session for scopes: ${scopeData.scopeStr} Error: ${e}`); @@ -354,6 +352,7 @@ export class AzureActiveDirectoryService { } const session = await this.exchangeCodeForSession(codeToExchange, codeVerifier, scopeData); + this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); return session; } @@ -690,6 +689,7 @@ export class AzureActiveDirectoryService { } const session = await this.exchangeCodeForSession(code, verifier, scopeData); + this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); resolve(session); } catch (err) { reject(err); @@ -716,6 +716,7 @@ export class AzureActiveDirectoryService { if (code) { inputBox.dispose(); const session = await this.exchangeCodeForSession(code, verifier, scopeData); + this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); resolve(session); } }); diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index cd9ae79d100..13322694966 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -183,10 +183,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806" integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/randombytes@^2.0.0": version "2.0.0" 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/npm/package.json b/extensions/npm/package.json index d87e2f1d016..e3dbfcfe26a 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@types/minimatch": "^3.0.3", - "@types/node": "16.x", + "@types/node": "18.x", "@types/which": "^2.0.0" }, "resolutions": { diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 26c6412b2d7..a066aac1e96 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -78,7 +78,7 @@ export async function activate(context: vscode.ExtensionContext): Promise } const lines = outputMatch.regexMatch[1]; - const fixes: vscode.TerminalQuickFixCommand[] = []; + const fixes: vscode.TerminalQuickFixExecuteTerminalCommand[] = []; for (const line of lines.split('\n')) { // search from the second char, since the lines might be prefixed with // "npm ERR!" which comes before the actual command suggestion. diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index cb1d0710210..cd465e173e6 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/which@^2.0.0": version "2.0.0" diff --git a/extensions/package.json b/extensions/package.json index f5f0b7e6197..b0bac44993d 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.6" + "typescript": "^5.2.0-dev.20230807" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index 8f9723197b2..8963fc796f4 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -77,7 +77,7 @@ "which": "^2.0.2" }, "devDependencies": { - "@types/node": "16.x", + "@types/node": "18.x", "@types/which": "^2.0.0" }, "repository": { diff --git a/extensions/php-language-features/yarn.lock b/extensions/php-language-features/yarn.lock index a418ca9f823..4c2e01e4b71 100644 --- a/extensions/php-language-features/yarn.lock +++ b/extensions/php-language-features/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/which@^2.0.0": version "2.0.0" diff --git a/extensions/references-view/package.json b/extensions/references-view/package.json index ba4cabed96a..a14e5a60ec0 100644 --- a/extensions/references-view/package.json +++ b/extensions/references-view/package.json @@ -402,6 +402,6 @@ "watch": "npx gulp watch-extension:references-view" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" } } diff --git a/extensions/references-view/yarn.lock b/extensions/references-view/yarn.lock index 4b3f8a1f7a0..8a3d10f2b65 100644 --- a/extensions/references-view/yarn.lock +++ b/extensions/references-view/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.33.tgz#566713b1b626f781c5c58fe3531307283e00720c" - integrity sha512-0PJ0vg+JyU0MIan58IOIFRtSvsb7Ri+7Wltx2qAg94eMOrpg4+uuP3aUHCpxXc1i0jCXiC+zIamSZh3l9AbcQA== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== 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]*#|\\])(? = (value: T | Promise) => void; + +const enum DeferredOutcome { + Resolved, + Rejected +} + +/** + * Copied from src\vs\base\common\async.ts + */ +export class DeferredPromise { + + private completeCallback!: ValueCallback; + private errorCallback!: (err: unknown) => void; + private outcome?: { outcome: DeferredOutcome.Rejected; value: any } | { outcome: DeferredOutcome.Resolved; value: T }; + + public get isRejected() { + return this.outcome?.outcome === DeferredOutcome.Rejected; + } + + public get isResolved() { + return this.outcome?.outcome === DeferredOutcome.Resolved; + } + + public get isSettled() { + return !!this.outcome; + } + + public get value() { + return this.outcome?.outcome === DeferredOutcome.Resolved ? this.outcome?.value : undefined; + } + + public readonly p: Promise; + + constructor() { + this.p = new Promise((c, e) => { + this.completeCallback = c; + this.errorCallback = e; + }); + } + + public complete(value: T) { + return new Promise(resolve => { + this.completeCallback(value); + this.outcome = { outcome: DeferredOutcome.Resolved, value }; + resolve(); + }); + } + + public error(err: unknown) { + return new Promise(resolve => { + this.errorCallback(err); + this.outcome = { outcome: DeferredOutcome.Rejected, value: err }; + resolve(); + }); + } +} diff --git a/extensions/tunnel-forwarding/src/extension.ts b/extensions/tunnel-forwarding/src/extension.ts new file mode 100644 index 00000000000..3dd6fdfef19 --- /dev/null +++ b/extensions/tunnel-forwarding/src/extension.ts @@ -0,0 +1,297 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { DeferredPromise } from './deferredPromise'; +import { splitNewLines } from './split'; + +export const enum TunnelPrivacyId { + Private = 'private', + Public = 'public', +} + +/** + * Timeout after the last port forwarding is disposed before we'll tear down + * the CLI. This is primarily used since privacy changes to port will appear + * as a dispose+re-create call, and we don't want to have to restart the CLI. + */ +const CLEANUP_TIMEOUT = 10_000; + +const cliPath = process.env.VSCODE_FORWARDING_IS_DEV + ? path.join(__dirname, '../../../cli/target/debug/code') + : path.join( + vscode.env.appRoot, + process.platform === 'win32' ? '../../bin' : 'bin', + vscode.env.appQuality === 'stable' ? 'code-tunnel' : 'code-tunnel-insiders', + ) + (process.platform === 'win32' ? '.exe' : ''); + +class Tunnel implements vscode.Tunnel { + private readonly disposeEmitter = new vscode.EventEmitter(); + public readonly onDidDispose = this.disposeEmitter.event; + public localAddress!: string; + + constructor( + public readonly remoteAddress: { port: number; host: string }, + public readonly privacy: TunnelPrivacyId, + ) { } + + public setPortFormat(formatString: string) { + this.localAddress = formatString.replace('{port}', String(this.remoteAddress.port)); + } + + dispose() { + this.disposeEmitter.fire(); + } +} + +const enum State { + Starting, + Active, + Inactive, + Error, +} + +type StateT = + | { state: State.Inactive } + | { state: State.Starting; process: ChildProcessWithoutNullStreams; cleanupTimeout?: NodeJS.Timeout } + | { state: State.Active; portFormat: string; process: ChildProcessWithoutNullStreams; cleanupTimeout?: NodeJS.Timeout } + | { state: State.Error; error: string }; + +export async function activate(context: vscode.ExtensionContext) { + if (vscode.env.remoteAuthority) { + return; // forwarding is local-only at the moment + } + + const logger = new Logger(vscode.l10n.t('Port Forwarding')); + const provider = new TunnelProvider(logger); + + context.subscriptions.push( + vscode.commands.registerCommand('tunnel-forwarding.showLog', () => logger.show()), + vscode.commands.registerCommand('tunnel-forwarding.restart', () => provider.restart()), + + provider.onDidStateChange(s => { + vscode.commands.executeCommand('setContext', 'tunnelForwardingIsRunning', s.state !== State.Inactive); + }), + + await vscode.workspace.registerTunnelProvider( + provider, + { + tunnelFeatures: { + elevation: false, + privacyOptions: [ + { themeIcon: 'globe', id: TunnelPrivacyId.Public, label: vscode.l10n.t('Public') }, + { themeIcon: 'lock', id: TunnelPrivacyId.Private, label: vscode.l10n.t('Private') }, + ], + }, + }, + ), + ); +} + +export function deactivate() { } + +class Logger { + private outputChannel?: vscode.LogOutputChannel; + + constructor(private readonly label: string) { } + + public show(): void { + return this.outputChannel?.show(); + } + + public clear() { + this.outputChannel?.clear(); + } + + public log( + logLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error', + message: string, + ...args: unknown[] + ) { + if (!this.outputChannel) { + this.outputChannel = vscode.window.createOutputChannel(this.label, { log: true }); + vscode.commands.executeCommand('setContext', 'tunnelForwardingHasLog', true); + } + this.outputChannel[logLevel](message, ...args); + } +} + +class TunnelProvider implements vscode.TunnelProvider { + private readonly tunnels = new Set(); + private readonly stateChange = new vscode.EventEmitter(); + private _state: StateT = { state: State.Inactive }; + + private get state(): StateT { + return this._state; + } + + private set state(state: StateT) { + this._state = state; + this.stateChange.fire(state); + } + + public readonly onDidStateChange = this.stateChange.event; + + constructor(private readonly logger: Logger) { } + + /** @inheritdoc */ + public async provideTunnel(tunnelOptions: vscode.TunnelOptions): Promise { + const tunnel = new Tunnel( + tunnelOptions.remoteAddress, + (tunnelOptions.privacy as TunnelPrivacyId) || TunnelPrivacyId.Private, + ); + + this.tunnels.add(tunnel); + tunnel.onDidDispose(() => { + this.tunnels.delete(tunnel); + this.updateActivePortsIfRunning(); + }); + + switch (this.state.state) { + case State.Error: + case State.Inactive: + await this.setupPortForwardingProcess(); + // fall through since state is now starting + case State.Starting: + this.updateActivePortsIfRunning(); + return new Promise((resolve, reject) => { + const l = this.stateChange.event(state => { + if (state.state === State.Active) { + tunnel.setPortFormat(state.portFormat); + l.dispose(); + resolve(tunnel); + } else if (state.state === State.Error) { + l.dispose(); + reject(new Error(state.error)); + } + }); + }); + case State.Active: + tunnel.setPortFormat(this.state.portFormat); + this.updateActivePortsIfRunning(); + return tunnel; + } + } + + /** Re/starts the port forwarding system. */ + public async restart() { + this.killRunningProcess(); + await this.setupPortForwardingProcess(); // will show progress + this.updateActivePortsIfRunning(); + } + + private isInStateWithProcess(process: ChildProcessWithoutNullStreams) { + return ( + (this.state.state === State.Starting || this.state.state === State.Active) && + this.state.process === process + ); + } + + private killRunningProcess() { + if (this.state.state === State.Starting || this.state.state === State.Active) { + this.logger.log('info', '[forwarding] no more ports, stopping forwarding CLI'); + this.state.process.kill(); + this.state = { state: State.Inactive }; + } + } + + private updateActivePortsIfRunning() { + if (this.state.state !== State.Starting && this.state.state !== State.Active) { + return; + } + + const ports = [...this.tunnels].map(t => ({ number: t.remoteAddress.port, privacy: t.privacy })); + this.state.process.stdin.write(`${JSON.stringify(ports)}\n`); + + if (ports.length === 0 && !this.state.cleanupTimeout) { + this.state.cleanupTimeout = setTimeout(() => this.killRunningProcess(), CLEANUP_TIMEOUT); + } else if (ports.length > 0 && this.state.cleanupTimeout) { + clearTimeout(this.state.cleanupTimeout); + this.state.cleanupTimeout = undefined; + } + } + + private async setupPortForwardingProcess() { + const session = await vscode.authentication.getSession('github', ['user:email', 'read:org'], { + createIfNone: true, + }); + + const args = [ + '--verbose', + 'tunnel', + 'forward-internal', + '--provider', + 'github', + '--access-token', + session.accessToken, + ]; + + this.logger.log('info', '[forwarding] starting CLI'); + const process = spawn(cliPath, args, { stdio: 'pipe' }); + this.state = { state: State.Starting, process }; + + const progressP = new DeferredPromise(); + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: vscode.l10n.t({ + comment: ['do not change link format [Show Log](command), only change the text "Show Log"'], + message: 'Starting port forwarding system ([Show Log]({0}))', + args: ['command:tunnel-forwarding.showLog'] + }), + }, + () => progressP.p, + ); + + let lastPortFormat: string | undefined; + process.on('exit', status => { + const msg = `[forwarding] exited with code ${status}`; + this.logger.log('info', msg); + progressP.complete(); // make sure to clear progress on unexpected exit + if (this.isInStateWithProcess(process)) { + this.state = { state: State.Error, error: msg }; + } + }); + + process.on('error', err => { + this.logger.log('error', `[forwarding] ${err}`); + progressP.complete(); // make sure to clear progress on unexpected exit + if (this.isInStateWithProcess(process)) { + this.state = { state: State.Error, error: String(err) }; + } + }); + + process.stdout + .pipe(splitNewLines()) + .on('data', line => this.logger.log('info', `[forwarding] ${line}`)) + .resume(); + + process.stderr + .pipe(splitNewLines()) + .on('data', line => { + try { + const l: { port_format: string } = JSON.parse(line); + if (l.port_format && l.port_format !== lastPortFormat) { + this.state = { + state: State.Active, + portFormat: l.port_format, process, + cleanupTimeout: 'cleanupTimeout' in this.state ? this.state.cleanupTimeout : undefined, + }; + progressP.complete(); + } + } catch (e) { + this.logger.log('error', `[forwarding] ${line}`); + } + }) + .resume(); + + await new Promise((resolve, reject) => { + process.on('spawn', resolve); + process.on('error', reject); + }); + } +} diff --git a/extensions/tunnel-forwarding/src/split.ts b/extensions/tunnel-forwarding/src/split.ts new file mode 100644 index 00000000000..6e9d7474604 --- /dev/null +++ b/extensions/tunnel-forwarding/src/split.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export const splitNewLines = () => new StreamSplitter('\n'.charCodeAt(0)); + +/** + * Copied and simplified from src\vs\base\node\nodeStreams.ts + */ +export class StreamSplitter extends Transform { + private buffer: Buffer | undefined; + + constructor(private readonly splitter: number) { + super(); + } + + 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 = this.buffer.indexOf(this.splitter, offset); + if (index === -1) { + break; + } + + this.push(this.buffer.subarray(offset, index + 1)); + offset = index + 1; + } + + this.buffer = offset === this.buffer.length ? undefined : this.buffer.subarray(offset); + callback(); + } + + override _flush(callback: (error?: Error | null, data?: any) => void): void { + if (this.buffer) { + this.push(this.buffer); + } + + callback(); + } +} diff --git a/extensions/tunnel-forwarding/tsconfig.json b/extensions/tunnel-forwarding/tsconfig.json new file mode 100644 index 00000000000..4769a9faec8 --- /dev/null +++ b/extensions/tunnel-forwarding/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out", + "downlevelIteration": true, + "types": [ + "node" + ] + }, + "include": [ + "src/**/*", + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", + "../../src/vscode-dts/vscode.proposed.tunnelFactory.d.ts" + ] +} diff --git a/extensions/tunnel-forwarding/yarn.lock b/extensions/tunnel-forwarding/yarn.lock new file mode 100644 index 00000000000..8a3d10f2b65 --- /dev/null +++ b/extensions/tunnel-forwarding/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/typescript-basics/package.json b/extensions/typescript-basics/package.json index 07631abc414..63c07e75b6d 100644 --- a/extensions/typescript-basics/package.json +++ b/extensions/typescript-basics/package.json @@ -55,8 +55,8 @@ }, { "id": "json", - "filenames": [ - "tsconfig.tsbuildinfo" + "extensions": [ + ".tsbuildinfo" ] } ], diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index c7cf530dbb9..d855af70e68 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -43,7 +43,7 @@ "vscode-uri": "^3.0.3" }, "devDependencies": { - "@types/node": "16.x", + "@types/node": "18.x", "@types/semver": "^5.5.0" }, "scripts": { @@ -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/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..5363619579b 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,21 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin return result; }); } + + private convertInlayHintText(resource: vscode.Uri, tsHint: Proto.InlayHintItem): string | vscode.InlayHintLabelPart[] { + if (tsHint.displayParts) { + return tsHint.displayParts.map((part): vscode.InlayHintLabelPart => { + const out = new vscode.InlayHintLabelPart(part.text); + if (part.span) { + out.location = Location.fromTextSpan(resource, part.span); + } + return out; + }); + } + + return tsHint.text; + + } } function fromProtocolInlayHintKind(kind: Proto.InlayHintKind): vscode.InlayHintKind | undefined { diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index a99b92685fc..ae36fbffb22 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -175,47 +175,80 @@ class MoveToFileRefactorCommand implements Command { if (response.type !== 'response' || !response.body) { return; } + const body = response.body; - const selectExistingFileItem: vscode.QuickPickItem = { - label: vscode.l10n.t("Select existing file..."), - }; - const selectNewFileItem: vscode.QuickPickItem = { - label: vscode.l10n.t("Enter new file path..."), - }; - - type DestinationItem = vscode.QuickPickItem & { readonly file: string }; + type DestinationItem = vscode.QuickPickItem & { readonly file?: string }; + const selectExistingFileItem: vscode.QuickPickItem = { label: vscode.l10n.t("Select existing file...") }; + const selectNewFileItem: vscode.QuickPickItem = { label: vscode.l10n.t("Enter new file path...") }; const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); - const destinationItems = response.body.files.map((file): DestinationItem => { - const uri = this.client.toResource(file); - const parentDir = Utils.dirname(uri); + const quickPick = vscode.window.createQuickPick(); + quickPick.ignoreFocusOut = true; - let description; - if (workspaceFolder) { - if (uri.scheme === Schemes.file) { - description = path.relative(workspaceFolder.uri.fsPath, parentDir.fsPath); - } else { - description = path.posix.relative(workspaceFolder.uri.path, parentDir.path); - } - } else { - description = parentDir.fsPath; + // true so we don't skip computing in the first call + let quickPickInRelativeMode = true; + const updateItems = () => { + const relativeQuery = ['./', '../'].find(str => quickPick.value.startsWith(str)); + if (quickPickInRelativeMode === false && !!relativeQuery === false) { + return; } + quickPickInRelativeMode = !!relativeQuery; + const destinationItems = body.files.map((file): DestinationItem | undefined => { + const uri = this.client.toResource(file); + const parentDir = Utils.dirname(uri); + const filename = Utils.basename(uri); - return { - file, - label: Utils.basename(uri), - description, - }; - }); + let description: string | undefined; + if (workspaceFolder) { + if (uri.scheme === Schemes.file) { + description = path.relative(workspaceFolder.uri.fsPath, parentDir.fsPath); + } else { + description = path.posix.relative(workspaceFolder.uri.path, parentDir.path); + } + if (relativeQuery) { + const convertRelativePath = (str: string) => { + return !str.startsWith('../') ? `./${str}` : str; + }; - const picked = await vscode.window.showQuickPick([ - selectExistingFileItem, - selectNewFileItem, - { label: vscode.l10n.t("Destination Files"), kind: vscode.QuickPickItemKind.Separator }, - ...destinationItems - ], { - title: vscode.l10n.t("Move to File"), - placeHolder: vscode.l10n.t("Select move destination"), + const relativePath = convertRelativePath(path.relative(path.dirname(document.uri.fsPath), uri.fsPath)); + if (!relativePath.startsWith(relativeQuery)) { + return; + } + description = relativePath; + } + } else { + description = parentDir.fsPath; + } + + return { + file, + label: Utils.basename(uri), + description: relativeQuery ? description : path.join(description, filename), + }; + }); + quickPick.items = [ + selectExistingFileItem, + selectNewFileItem, + { label: vscode.l10n.t("Destination Files"), kind: vscode.QuickPickItemKind.Separator }, + ...coalesce(destinationItems) + ]; + }; + quickPick.title = vscode.l10n.t("Move to File"); + quickPick.placeholder = vscode.l10n.t("Enter file path"); + quickPick.matchOnDescription = true; + quickPick.onDidChangeValue(updateItems); + updateItems(); + + const picked = await new Promise(resolve => { + quickPick.onDidAccept(() => { + resolve(quickPick.selectedItems[0]); + quickPick.dispose(); + }); + quickPick.onDidHide(() => { + resolve(undefined); + quickPick.dispose(); + }); + quickPick.show(); }); if (!picked) { return; @@ -236,7 +269,7 @@ class MoveToFileRefactorCommand implements Command { }); return picked ? this.client.toTsFilePath(picked) : undefined; } else { - return (picked as DestinationItem).file; + return picked.file; } } } 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 2b8e967e6cb..1b011de0449 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-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/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== "@types/semver@^5.5.0": version "5.5.0" diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index b230a1a4897..aed0d546ce2 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", @@ -214,7 +214,7 @@ }, "devDependencies": { "@types/mocha": "^9.1.1", - "@types/node": "16.x" + "@types/node": "18.x" }, "repository": { "type": "git", diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/documentPaste.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/documentPaste.test.ts index dd5b9eae41c..c2cdd073d74 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/documentPaste.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/documentPaste.test.ts @@ -37,7 +37,7 @@ suite.skip('vscode API - Copy Paste', function () { dataTransfer.set(textPlain, new vscode.DataTransferItem(reversed)); } } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); const newDocContent = getNextDocumentText(testDisposables, doc); @@ -62,7 +62,7 @@ suite.skip('vscode API - Copy Paste', function () { dataTransfer.set(textPlain, new vscode.DataTransferItem(reversed + '\n')); } } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); const newDocContent = getNextDocumentText(testDisposables, doc); @@ -88,7 +88,7 @@ suite.skip('vscode API - Copy Paste', function () { dataTransfer.set(textPlain, new vscode.DataTransferItem(`(${ranges.length})${selections.join(' ')}`)); } } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); editor.selections = [new vscode.Selection(0, 0, 0, 0)]; @@ -118,7 +118,7 @@ suite.skip('vscode API - Copy Paste', function () { dataTransfer.set(textPlain, new vscode.DataTransferItem('a')); providerAResolve(); } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); // Later registered providers will be called first testDisposables.push(vscode.languages.registerDocumentPasteEditProvider({ language: 'plaintext' }, new class implements vscode.DocumentPasteEditProvider { @@ -132,7 +132,7 @@ suite.skip('vscode API - Copy Paste', function () { dataTransfer.set(textPlain, new vscode.DataTransferItem('b')); } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); const newDocContent = getNextDocumentText(testDisposables, doc); @@ -159,7 +159,7 @@ suite.skip('vscode API - Copy Paste', function () { dataTransfer.set(textPlain, new vscode.DataTransferItem('xyz')); providerAResolve(); } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); testDisposables.push(vscode.languages.registerDocumentPasteEditProvider({ language: 'plaintext' }, new class implements vscode.DocumentPasteEditProvider { async prepareDocumentPaste(_document: vscode.TextDocument, _ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise { @@ -172,7 +172,7 @@ suite.skip('vscode API - Copy Paste', function () { const str = await entry!.asString(); dataTransfer.set(textPlain, new vscode.DataTransferItem(reverseString(str))); } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); const newDocContent = getNextDocumentText(testDisposables, doc); @@ -192,13 +192,13 @@ suite.skip('vscode API - Copy Paste', function () { async prepareDocumentPaste(_document: vscode.TextDocument, _ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise { dataTransfer.set(textPlain, new vscode.DataTransferItem('xyz')); } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); testDisposables.push(vscode.languages.registerDocumentPasteEditProvider({ language: 'plaintext' }, new class implements vscode.DocumentPasteEditProvider { async prepareDocumentPaste(_document: vscode.TextDocument, _ranges: readonly vscode.Range[], _dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise { throw new Error('Expected testing error from bad provider'); } - }, { copyMimeTypes: [textPlain] })); + }, { id: 'test', copyMimeTypes: [textPlain] })); await vscode.commands.executeCommand('editor.action.clipboardCopyAction'); const newDocContent = getNextDocumentText(testDisposables, doc); 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..f8c8b806853 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,52 @@ 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('should listen to all future terminal data events', function (done) { + // This test has been flaky in the past but it's not clear why, possibly because + // events from previous tests polluting the event recording in this test. Retries + // was added so we continue to have coverage of the onDidWriteTerminalData API. + this.retries(3); + const openEvents: string[] = []; const dataEvents: { name: string; data: string }[] = []; const closeEvents: string[] = []; @@ -897,6 +941,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-api-tests/yarn.lock b/extensions/vscode-api-tests/yarn.lock index b856762f8e3..3c35048cdc7 100644 --- a/extensions/vscode-api-tests/yarn.lock +++ b/extensions/vscode-api-tests/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index e2517c811f2..8cb8442b6f2 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -20,7 +20,7 @@ "jsonc-parser": "2.2.1" }, "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "contributes": { "semanticTokenTypes": [ 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/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index 44d4327771e..4c166d0a3c8 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== jsonc-parser@2.2.1: version "2.2.1" diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index e721c65567a..e538d43f21b 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -33,7 +33,7 @@ "main": "./out/extension", "browser": "./dist/browser/testResolverMain", "devDependencies": { - "@types/node": "16.x" + "@types/node": "18.x" }, "capabilities": { "untrustedWorkspaces": { diff --git a/extensions/vscode-test-resolver/yarn.lock b/extensions/vscode-test-resolver/yarn.lock index e724e7fffa3..8a3d10f2b65 100644 --- a/extensions/vscode-test-resolver/yarn.lock +++ b/extensions/vscode-test-resolver/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@types/node@16.x": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/node@18.x": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 18507f58b59..815d839a79a 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.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== +typescript@^5.2.0-dev.20230807: + version "5.2.0-dev.20230807" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230807.tgz#652fa8a3fcc46958035d9b911c815299deb9f58e" + integrity sha512-sF8sZl3r/mpAdKAxASaWaoU+mNPF3g8OrZ601HraU2l4WEwAf6nO7sq0Bzl+Y3FV7sWjy+gb6kdM1CtLjVne/g== vscode-grammar-updater@^1.1.0: version "1.1.0" diff --git a/package.json b/package.json index 9fba1bf8246..22b189fb441 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.81.0", - "distro": "bed7b201bc6ebbfc63268cb4c390d788a86b0192", + "version": "1.82.0", + "distro": "32d09c1ac0a6f84901cd96b1f6f800f3f284fe36", "author": { "name": "Microsoft Corporation" }, @@ -65,14 +65,14 @@ "perf": "node scripts/code-perf.js" }, "dependencies": { - "@microsoft/1ds-core-js": "^3.2.2", - "@microsoft/1ds-post-js": "^3.2.2", + "@microsoft/1ds-core-js": "^4.0.1", + "@microsoft/1ds-post-js": "^4.0.1", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.4", - "@vscode/proxy-agent": "^0.15.0", + "@vscode/proxy-agent": "^0.17.2", "@vscode/ripgrep": "^1.15.5", - "@vscode/spdlog": "^0.13.10", + "@vscode/spdlog": "^0.13.11", "@vscode/sqlite3": "5.1.6-vscode", "@vscode/sudo-prompt": "9.3.1", "@vscode/vscode-languagedetection": "1.0.21", @@ -83,10 +83,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.7.0", - "native-keymap": "^3.3.2", + "native-keymap": "^3.3.4", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta1", "tas-client-umd": "0.1.8", @@ -94,14 +95,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.7", - "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.2", - "xterm-addon-search": "0.13.0-beta.2", - "xterm-addon-serialize": "0.11.0-beta.2", + "xterm": "5.3.0-beta.28", + "xterm-addon-canvas": "0.5.0-beta.8", + "xterm-addon-image": "0.5.0", + "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.7", + "xterm-addon-webgl": "0.16.0-beta.7", + "xterm-headless": "5.3.0-beta.28", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, @@ -116,10 +117,11 @@ "@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", - "@types/node": "16.x", + "@types/node": "18.x", "@types/sinon": "^10.0.2", "@types/sinon-test": "^2.4.2", "@types/trusted-types": "^1.0.6", @@ -148,7 +150,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "22.3.14", + "electron": "25.4.0", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", @@ -210,7 +212,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.2.0-dev.20230710", + "typescript": "^5.2.0-dev.20230807", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/product.json b/product.json index 6d015e98bd0..2ae3ee8f0f8 100644 --- a/product.json +++ b/product.json @@ -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/.yarnrc b/remote/.yarnrc index 29d716339bf..cc073758831 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,5 +1,5 @@ disturl "http://nodejs.org/dist" -target "16.17.1" -ms_build_id "220517" +target "18.15.0" +ms_build_id "223745" runtime "node" build_from_source "true" diff --git a/remote/package.json b/remote/package.json index 9b92d007a47..9382ef726b2 100644 --- a/remote/package.json +++ b/remote/package.json @@ -3,13 +3,13 @@ "version": "0.0.0", "private": true, "dependencies": { - "@microsoft/1ds-core-js": "^3.2.2", - "@microsoft/1ds-post-js": "^3.2.2", + "@microsoft/1ds-core-js": "^4.0.1", + "@microsoft/1ds-post-js": "^4.0.1", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.15.0", + "@vscode/proxy-agent": "^0.17.2", "@vscode/ripgrep": "^1.15.5", - "@vscode/spdlog": "^0.13.10", + "@vscode/spdlog": "^0.13.11", "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-process-tree": "^0.5.0", "@vscode/windows-registry": "^1.1.0", @@ -18,6 +18,7 @@ "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.7", - "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.2", - "xterm-addon-search": "0.13.0-beta.2", - "xterm-addon-serialize": "0.11.0-beta.2", + "xterm": "5.3.0-beta.28", + "xterm-addon-canvas": "0.5.0-beta.8", + "xterm-addon-image": "0.5.0", + "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.7", + "xterm-addon-webgl": "0.16.0-beta.7", + "xterm-headless": "5.3.0-beta.28", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index 64b5c929970..42c42a7672a 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -3,19 +3,19 @@ "version": "0.0.0", "private": true, "dependencies": { - "@microsoft/1ds-core-js": "^3.2.2", - "@microsoft/1ds-post-js": "^3.2.2", + "@microsoft/1ds-core-js": "^4.0.1", + "@microsoft/1ds-post-js": "^4.0.1", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/vscode-languagedetection": "1.0.21", "jschardet": "3.0.0", "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.7", - "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.2", - "xterm-addon-search": "0.13.0-beta.2", + "xterm": "5.3.0-beta.28", + "xterm-addon-canvas": "0.5.0-beta.8", + "xterm-addon-image": "0.5.0", + "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.7" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index e2a8a2d0fbe..27c44cbcdc3 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,41 +2,63 @@ # yarn lockfile v1 -"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.2": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" - integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== +"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec" + integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.4" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "^3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.6 < 2.x" -"@microsoft/1ds-post-js@^3.2.2": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" - integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== +"@microsoft/1ds-post-js@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e" + integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ== dependencies: - "@microsoft/1ds-core-js" "3.2.3" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/1ds-core-js" "4.0.1" + "@microsoft/applicationinsights-shims" "^3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.6 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.4": - version "2.8.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" - integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== dependencies: - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" - integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== +"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== + dependencies: + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@microsoft/dynamicproto-js@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" - integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== + dependencies: + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0" + integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA== + dependencies: + "@nevware21/ts-utils" ">= 0.9.7 < 2.x" + +"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x": + version "0.9.8" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8" + integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA== "@vscode/iconv-lite-umd@0.7.0": version "0.7.0" @@ -68,32 +90,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.8: + version "0.5.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.8.tgz#243d1161cc68441a1d531f25e9c4f1d6b3cbe79d" + integrity sha512-gCqoqFqfc4N8YZLlj3U5+247r3pjLc9ERRHpuBWSJF2GF623LLk4aSjexnEE0M1b7rIDa3xSKo5limHQXx2mWg== -xterm-addon-image@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.2.tgz#d25bb3a8c543d014accbfbbc875927f54d5f43b9" - integrity sha512-QVuH5CGlValtFMVorb71TnMV7kQLqc9+V5oyOMNvFqW1NiOBNo/d0WVvMU8qOd2vsAdTbhjvUO8SPptZ5TJHYw== +xterm-addon-image@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.5.0.tgz#3c9bb332a3de55ab200dbefd3411e3b0d985314f" + integrity sha512-bWXUBeDzhisYh0clVKx4JgQrZjpn+/QRMRwNsfnRpjCMhgmZ+SL3Bivktd7q03O4uKMMcAOe6bSmppwP9/um0Q== -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.7: + version "0.16.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.7.tgz#d9febd140e2020e05c39e66214dae685769144ed" + integrity sha512-g657v/ah5JSjeTnHL3myCxiqPx0cA1J70kWXCqP4rsb1nPPlXtbq69cH5AsV/mNN5jnXjd6vHPa7CWt3ptOSgQ== -xterm@5.3.0-beta.7: - version "5.3.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.7.tgz#ee638fe60382e6b13f36dec2355c3530ff1c4c34" - integrity sha512-wd+QHHfCVSZMSH7ah8WSeCu7VAqZcAD5pEAOiQxCg4VfXu+L//QOycqlWecgr0SFui2X9LshfD+cI9J3FcHRNA== +xterm@5.3.0-beta.28: + version "5.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.28.tgz#1a5176fa6d9f424913a85dd0371d7384d2f882db" + integrity sha512-WA7aX2nE+ptxU4EConXGf0vKwkRisaXNt8ExEw/MRnfDudpZo2J30raJ2SQV2n/xWCGoeVIE5njYmR/hhiGOQA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 8084ab6e27a..fa9597cee0a 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -2,41 +2,63 @@ # yarn lockfile v1 -"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.2": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" - integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== +"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec" + integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.4" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "^3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.6 < 2.x" -"@microsoft/1ds-post-js@^3.2.2": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" - integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== +"@microsoft/1ds-post-js@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e" + integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ== dependencies: - "@microsoft/1ds-core-js" "3.2.3" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/1ds-core-js" "4.0.1" + "@microsoft/applicationinsights-shims" "^3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.6 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.4": - version "2.8.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" - integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== dependencies: - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" - integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== +"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== + dependencies: + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@microsoft/dynamicproto-js@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" - integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== + dependencies: + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0" + integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA== + dependencies: + "@nevware21/ts-utils" ">= 0.9.7 < 2.x" + +"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x": + version "0.9.8" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8" + integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA== "@parcel/watcher@2.1.0": version "2.1.0" @@ -48,27 +70,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.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.15.0.tgz#b8fb8b89180a71295a8f8682775f69ab1dcf6860" - integrity sha512-HpD4A9CUOwKbC6vLa0+MEsCo/qlgbue9U9s8Z7NzJDdf2YEGjUaYf9Mvj5T1LhJX20Hv1COvkGcc7zPhtIbgbA== +"@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" @@ -80,10 +102,10 @@ https-proxy-agent "^5.0.0" proxy-from-env "^1.1.0" -"@vscode/spdlog@^0.13.10": - version "0.13.10" - resolved "https://registry.yarnpkg.com/@vscode/spdlog/-/spdlog-0.13.10.tgz#5476853b968a1bcc389b92175d11e636464858e8" - integrity sha512-BHJN/r2XurLDR0doBhyQ5b+DUjFjqwnOcD4ZjW/7MkuShO+Wn5KVKOl6x/xQLCAlOlQqVVe42n2A7FwuIFpkWw== +"@vscode/spdlog@^0.13.11": + version "0.13.11" + resolved "https://registry.yarnpkg.com/@vscode/spdlog/-/spdlog-0.13.11.tgz#1c65c3527dd8e9679ada7f427c4d9e4af2ea097a" + integrity sha512-4qk6G3XKTJNsrqsGwmo9lr8NPaH84WeLsoFVPrpvoEC1t1tFu7H6l4RRMgzKNeE92nwRbjejLv1nATJNoabRfA== dependencies: bindings "^1.5.0" mkdirp "^0.5.5" @@ -120,7 +142,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 +156,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 +266,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" @@ -356,14 +385,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 +409,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 +432,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 +476,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 +626,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" @@ -691,27 +754,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 +899,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.8: + version "0.5.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.8.tgz#243d1161cc68441a1d531f25e9c4f1d6b3cbe79d" + integrity sha512-gCqoqFqfc4N8YZLlj3U5+247r3pjLc9ERRHpuBWSJF2GF623LLk4aSjexnEE0M1b7rIDa3xSKo5limHQXx2mWg== -xterm-addon-image@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.2.tgz#d25bb3a8c543d014accbfbbc875927f54d5f43b9" - integrity sha512-QVuH5CGlValtFMVorb71TnMV7kQLqc9+V5oyOMNvFqW1NiOBNo/d0WVvMU8qOd2vsAdTbhjvUO8SPptZ5TJHYw== +xterm-addon-image@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.5.0.tgz#3c9bb332a3de55ab200dbefd3411e3b0d985314f" + integrity sha512-bWXUBeDzhisYh0clVKx4JgQrZjpn+/QRMRwNsfnRpjCMhgmZ+SL3Bivktd7q03O4uKMMcAOe6bSmppwP9/um0Q== -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.7: + version "0.16.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.7.tgz#d9febd140e2020e05c39e66214dae685769144ed" + integrity sha512-g657v/ah5JSjeTnHL3myCxiqPx0cA1J70kWXCqP4rsb1nPPlXtbq69cH5AsV/mNN5jnXjd6vHPa7CWt3ptOSgQ== -xterm-headless@5.3.0-beta.7: - version "5.3.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.7.tgz#92704cecf937a18f2223b16efcab8c4e9e8a9e62" - integrity sha512-8LDY/cId73q3w6jMmiGU6HtJlfIhtqu/oWlDj/9wUr8SQnCQJwPBzu6RizzQRz1lzMN+8WHdzrHjKcNtayZbdw== +xterm-headless@5.3.0-beta.28: + version "5.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.28.tgz#c0307c7363fe290fc5edb68b81135d6c37c76f67" + integrity sha512-2zZFNa6AOpoAcMEgzzV7ilSv2qUecs8gSJbfxVV9D4lbNuwWYW5AV8VEdfKnD8TRPbsC/5Uc4RZ82qhJNYOC1g== -xterm@5.3.0-beta.7: - version "5.3.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.7.tgz#ee638fe60382e6b13f36dec2355c3530ff1c4c34" - integrity sha512-wd+QHHfCVSZMSH7ah8WSeCu7VAqZcAD5pEAOiQxCg4VfXu+L//QOycqlWecgr0SFui2X9LshfD+cI9J3FcHRNA== +xterm@5.3.0-beta.28: + version "5.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.28.tgz#1a5176fa6d9f424913a85dd0371d7384d2f882db" + integrity sha512-WA7aX2nE+ptxU4EConXGf0vKwkRisaXNt8ExEw/MRnfDudpZo2J30raJ2SQV2n/xWCGoeVIE5njYmR/hhiGOQA== 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.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 8917223094a..f03de33fb3d 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -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/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/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index 9cab3604196..4f42810f0bd 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { COI } from 'vs/base/common/network'; import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; @@ -85,11 +86,13 @@ function isPromiseLike(obj: any): obj is PromiseLike { */ class WebWorker implements IWorker { - private id: number; + private readonly id: number; + private readonly label: string; private worker: Promise | null; constructor(moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) { this.id = id; + this.label = label; const workerOrPromise = getWorker(label); if (isPromiseLike(workerOrPromise)) { this.worker = workerOrPromise; @@ -113,7 +116,14 @@ class WebWorker implements IWorker { } public postMessage(message: any, transfer: Transferable[]): void { - this.worker?.then(w => w.postMessage(message, transfer)); + this.worker?.then(w => { + try { + w.postMessage(message, transfer); + } catch (err) { + onUnexpectedError(err); + onUnexpectedError(new Error(`FAILED to post message to '${this.label}'-worker`, { cause: err })); + } + }); } public dispose(): void { diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 249a479504b..a72e8e0ea85 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -15,7 +15,6 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { FileAccess, RemoteAuthorities, Schemas } from 'vs/base/common/network'; import * as platform from 'vs/base/common/platform'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; export function clearNode(node: HTMLElement): void { @@ -1284,7 +1283,7 @@ export function triggerUpload(): Promise { // Resolve once the input event has fired once event.Event.once(event.Event.fromDOMEventEmitter(input, 'input'))(() => { - resolve(withNullAsUndefined(input.files)); + resolve(input.files ?? undefined); }); input.click(); 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/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index c09670b4224..fe70a35dd44 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -203,8 +203,8 @@ export class ActionBar extends Disposable implements IActionRunner { this._register(this.focusTracker.onDidBlur(() => { if (DOM.getActiveElement() === this.domNode || !DOM.isAncestor(DOM.getActiveElement(), this.domNode)) { this._onDidBlur.fire(); + this.previouslyFocusedItem = this.focusedItem; this.focusedItem = undefined; - this.previouslyFocusedItem = undefined; this.triggerKeyDown = false; } })); 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/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index c4a33a4d566..b5623b0a55c 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/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/hover/hover.css b/src/vs/base/browser/ui/hover/hover.css index 68907c6a062..5e12d070057 100644 --- a/src/vs/base/browser/ui/hover/hover.css +++ b/src/vs/base/browser/ui/hover/hover.css @@ -28,7 +28,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/hover/hoverWidget.ts b/src/vs/base/browser/ui/hover/hoverWidget.ts index 96e99cbec4a..032af07dbd5 100644 --- a/src/vs/base/browser/ui/hover/hoverWidget.ts +++ b/src/vs/base/browser/ui/hover/hoverWidget.ts @@ -9,6 +9,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import 'vs/css!./hover'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -94,3 +95,7 @@ export class HoverAction extends Disposable { } } } + +export function getHoverAriaLabel(shouldHaveHint?: boolean, keybinding?: string | null): string | undefined { + return shouldHaveHint && keybinding ? localize('acessibleViewHint', "Inspect this in the accessible view with {0}.", keybinding) : shouldHaveHint ? localize('acessibleViewHintNoKbOpen', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.") : ''; +} diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 599abeaf6a1..980be904fa4 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -120,6 +120,7 @@ export class IconLabel extends Disposable { setLabel(label: string | string[], description?: string, options?: IIconLabelValueOptions): void { const labelClasses = ['monaco-icon-label']; const containerClasses = ['monaco-icon-label-container']; + let ariaLabel: string = ''; if (options) { if (options.extraClasses) { labelClasses.push(...options.extraClasses); @@ -136,9 +137,13 @@ export class IconLabel extends Disposable { if (options.disabledCommand) { containerClasses.push('disabled'); } + if (options.title) { + ariaLabel += options.title; + } } this.domNode.className = labelClasses.join(' '); + this.domNode.element.setAttribute('aria-label', ariaLabel); this.labelContainer.className = containerClasses.join(' '); this.setupHover(options?.descriptionTitle ? this.labelContainer : this.element, options?.title); 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/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e8ec5372e18..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[]; @@ -1354,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/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/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index fe7586f0d56..061003dbc9a 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -20,7 +20,6 @@ import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import { withNullAsUndefined } from 'vs/base/common/types'; import 'vs/css!./menubar'; import * as nls from 'vs/nls'; @@ -1020,7 +1019,7 @@ export class MenuBar extends Disposable { getKeyBinding: this.options.getKeybinding, actionRunner: this.actionRunner, enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics), - ariaLabel: withNullAsUndefined(customMenu.buttonElement.getAttribute('aria-label')), + ariaLabel: customMenu.buttonElement.getAttribute('aria-label') ?? undefined, expandDirection: this.isCompact ? this.options.compactMode : Direction.Right, useEventAsContext: true }; 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/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 85936d72ce6..c91d4ee1b07 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -13,7 +13,6 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { EventMultiplexer } from 'vs/base/common/event'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { withNullAsUndefined } from 'vs/base/common/types'; import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; @@ -196,7 +195,7 @@ export class ToolBar extends Disposable { private getKeybindingLabel(action: IAction): string | undefined { const key = this.lookupKeybindings ? this.options.getKeyBinding?.(action) : undefined; - return withNullAsUndefined(key?.getLabel()); + return key?.getLabel() ?? undefined; } private clear(): void { 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/arrays.ts b/src/vs/base/common/arrays.ts index c22e14f3b7d..a54c2c59f9c 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -398,14 +398,14 @@ export function uniqueFilter(keyFn: (t: T) => R): (t: T) => boolean { } export function findLast(arr: readonly T[], predicate: (item: T) => boolean): T | undefined { - const idx = lastIndex(arr, predicate); + const idx = findLastIndex(arr, predicate); if (idx === -1) { return undefined; } return arr[idx]; } -export function lastIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { +export function findLastIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { for (let i = array.length - 1; i >= 0; i--) { const element = array[i]; @@ -707,6 +707,10 @@ export function tieBreakComparators(...comparators: Comparator[]): */ export const numberComparator: Comparator = (a, b) => a - b; +export function reverseOrder(comparator: Comparator): Comparator { + return (a, b) => -comparator(a, b); +} + /** * Returns the first item that is equal to or greater than every other item. */ 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/codicons.ts b/src/vs/base/common/codicons.ts index f205af33b4c..48dd3abfb72 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -60,6 +60,7 @@ export const Codicon = { tag: register('tag', 0xea66), tagAdd: register('tag-add', 0xea66), tagRemove: register('tag-remove', 0xea66), + gitPullRequestLabel: register('git-pull-request-label', 0xea66), person: register('person', 0xea67), personFollow: register('person-follow', 0xea67), personOutline: register('person-outline', 0xea67), @@ -314,6 +315,7 @@ export const Codicon = { megaphone: register('megaphone', 0xeb1e), mention: register('mention', 0xeb1f), milestone: register('milestone', 0xeb20), + gitPullRequestMilestone: register('git-pull-request-milestone', 0xeb20), mortarBoard: register('mortar-board', 0xeb21), move: register('move', 0xeb22), multipleWindows: register('multiple-windows', 0xeb23), @@ -438,9 +440,11 @@ export const Codicon = { menu: register('menu', 0xeb94), expandAll: register('expand-all', 0xeb95), feedback: register('feedback', 0xeb96), + gitPullRequestReviewer: register('git-pull-request-reviewer', 0xeb96), groupByRefType: register('group-by-ref-type', 0xeb97), ungroupByRefType: register('ungroup-by-ref-type', 0xeb98), account: register('account', 0xeb99), + gitPullRequestAssignee: register('git-pull-request-assignee', 0xeb99), bellDot: register('bell-dot', 0xeb9a), debugConsole: register('debug-console', 0xeb9b), library: register('library', 0xeb9c), diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 95def40789d..8fdf74b05be 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -107,6 +107,6 @@ export class SetMap { if (!values) { return new Set(); } - return new Set(values); + return values; } } 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..b62f09f07ca 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -58,9 +58,9 @@ export class MarkdownString implements IMarkdownString { appendText(value: string, newlineStyle: MarkdownStringTextNewlineStyle = MarkdownStringTextNewlineStyle.Paragraph): MarkdownString { 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(/([ \t]+)/g, (_match, g1) => ' '.repeat(g1.length)) // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. + .replace(/\>/gm, '\\>') // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. + .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..d35246667bc 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -190,20 +190,6 @@ export function validateConstraint(arg: unknown, constraint: TypeConstraint | un } } -/** - * Converts null to undefined, passes all other values through. - */ -export function withNullAsUndefined(x: T | null): T | undefined { - return x === null ? undefined : x; -} - -/** - * Converts undefined to null, passes all other values through. - */ -export function withUndefinedAsNull(x: T | undefined): T | null { - return typeof x === 'undefined' ? null : x; -} - type AddFirstParameterToFunction = T extends (...args: any[]) => TargetFunctionsReturnType ? // Function: add param to function (firstArg: FirstParameter, ...args: Parameters) => ReturnType : @@ -225,6 +211,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/powershell.ts b/src/vs/base/node/powershell.ts index be6aeb7f4b8..c24c14f7c94 100644 --- a/src/vs/base/node/powershell.ts +++ b/src/vs/base/node/powershell.ts @@ -22,7 +22,6 @@ const enum Arch { let processArch: Arch; switch (process.arch) { case 'ia32': - case 'x32': processArch = Arch.x86; break; case 'arm': diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index 6b2eb1cb820..40b3d5c46d3 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -5,7 +5,6 @@ import { BrowserWindow, IpcMainEvent, Menu, MenuItem } from 'electron'; import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { CONTEXT_MENU_CHANNEL, CONTEXT_MENU_CLOSE_CHANNEL, IPopupOptions, ISerializableContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; export function registerContextMenuListener(): void { @@ -13,7 +12,7 @@ export function registerContextMenuListener(): void { const menu = createMenu(event, onClickChannel, items); menu.popup({ - window: withNullAsUndefined(BrowserWindow.fromWebContents(event.sender)), + window: BrowserWindow.fromWebContents(event.sender) ?? undefined, x: options ? options.x : undefined, y: options ? options.y : undefined, positioningItem: options ? options.positioningItem : undefined, 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/ipc/electron-main/ipcMain.ts b/src/vs/base/parts/ipc/electron-main/ipcMain.ts index 95e8e4e1c32..6fe43ab0b3a 100644 --- a/src/vs/base/parts/ipc/electron-main/ipcMain.ts +++ b/src/vs/base/parts/ipc/electron-main/ipcMain.ts @@ -111,8 +111,12 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { const sender = event.senderFrame; const url = sender.url; - if (!url) { - return true; // TODO@electron this only seems to happen from playwright runs (https://github.com/microsoft/vscode/issues/147301) + // `url` can be `undefined` when running tests from playwright https://github.com/microsoft/vscode/issues/147301 + // and `url` can be `about:blank` when reloading the window + // from performance tab of devtools https://github.com/electron/electron/issues/39427. + // It is fine to skip the checks in these cases. + if (!url || url === 'about:blank') { + return true; } let host = 'unknown'; diff --git a/src/vs/base/parts/sandbox/common/electronTypes.ts b/src/vs/base/parts/sandbox/common/electronTypes.ts index 814edae59b2..f8c7a35e077 100644 --- a/src/vs/base/parts/sandbox/common/electronTypes.ts +++ b/src/vs/base/parts/sandbox/common/electronTypes.ts @@ -17,12 +17,11 @@ export interface MessageBoxOptions { */ message: string; /** - * Can be `"none"`, `"info"`, `"error"`, `"question"` or `"warning"`. On Windows, - * `"question"` displays the same icon as `"info"`, unless you set an icon using - * the `"icon"` option. On macOS, both `"warning"` and `"error"` display the same - * warning icon. + * Can be `none`, `info`, `error`, `question` or `warning`. On Windows, `question` + * displays the same icon as `info`, unless you set an icon using the `icon` + * option. On macOS, both `warning` and `error` display the same warning icon. */ - type?: string; + type?: ('none' | 'info' | 'error' | 'question' | 'warning'); /** * Array of texts for buttons. On Windows, an empty array will result in one button * labeled "OK". 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/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/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 212689e3c33..bd6fb95a75d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -934,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..9e6793fd489 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -13,6 +13,8 @@ import { ICredentialsMainService } from 'vs/platform/credentials/common/credenti 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 +58,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 +72,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 +145,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 = await this.credentialsService.getPassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash) ?? undefined; + 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 +181,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 +230,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/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 0065a8da62e..a003267c043 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -192,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)); @@ -212,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 d8b6d791e74..f911b0da431 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -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/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index bbd250053d9..0215f9031a6 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -29,7 +29,7 @@ const enum HitTestResultType { class UnknownHitTestResult { readonly type = HitTestResultType.Unknown; constructor( - readonly hitTarget: Element | null = null + readonly hitTarget: HTMLElement | null = null ) { } } @@ -72,31 +72,31 @@ export class MouseTarget { } return range ?? null; } - public static createUnknown(element: Element | null, mouseColumn: number, position: Position | null): IMouseTargetUnknown { + public static createUnknown(element: HTMLElement | null, mouseColumn: number, position: Position | null): IMouseTargetUnknown { return { type: MouseTargetType.UNKNOWN, element, mouseColumn, position, range: this._deduceRage(position) }; } - public static createTextarea(element: Element | null, mouseColumn: number): IMouseTargetTextarea { + public static createTextarea(element: HTMLElement | null, mouseColumn: number): IMouseTargetTextarea { return { type: MouseTargetType.TEXTAREA, element, mouseColumn, position: null, range: null }; } - public static createMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, element: Element | null, mouseColumn: number, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin { + public static createMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, element: HTMLElement | null, mouseColumn: number, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin { return { type, element, mouseColumn, position, range, detail }; } - public static createViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, element: Element | null, mouseColumn: number, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone { + public static createViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, element: HTMLElement | null, mouseColumn: number, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone { return { type, element, mouseColumn, position, range: this._deduceRage(position), detail }; } - public static createContentText(element: Element | null, mouseColumn: number, position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText { + public static createContentText(element: HTMLElement | null, mouseColumn: number, position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText { return { type: MouseTargetType.CONTENT_TEXT, element, mouseColumn, position, range: this._deduceRage(position, range), detail }; } - public static createContentEmpty(element: Element | null, mouseColumn: number, position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty { + public static createContentEmpty(element: HTMLElement | null, mouseColumn: number, position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty { return { type: MouseTargetType.CONTENT_EMPTY, element, mouseColumn, position, range: this._deduceRage(position), detail }; } - public static createContentWidget(element: Element | null, mouseColumn: number, detail: string): IMouseTargetContentWidget { + public static createContentWidget(element: HTMLElement | null, mouseColumn: number, detail: string): IMouseTargetContentWidget { return { type: MouseTargetType.CONTENT_WIDGET, element, mouseColumn, position: null, range: null, detail }; } - public static createScrollbar(element: Element | null, mouseColumn: number, position: Position): IMouseTargetScrollbar { + public static createScrollbar(element: HTMLElement | null, mouseColumn: number, position: Position): IMouseTargetScrollbar { return { type: MouseTargetType.SCROLLBAR, element, mouseColumn, position, range: this._deduceRage(position) }; } - public static createOverlayWidget(element: Element | null, mouseColumn: number, detail: string): IMouseTargetOverlayWidget { + public static createOverlayWidget(element: HTMLElement | null, mouseColumn: number, detail: string): IMouseTargetOverlayWidget { return { type: MouseTargetType.OVERLAY_WIDGET, element, mouseColumn, position: null, range: null, detail }; } public static createOutsideEditor(mouseColumn: number, position: Position, outsidePosition: 'above' | 'below' | 'left' | 'right', outsideDistance: number): IMouseTargetOutsideEditor { @@ -389,10 +389,10 @@ abstract class BareHitTestRequest { class HitTestRequest extends BareHitTestRequest { private readonly _ctx: HitTestContext; - public readonly target: Element | null; + public readonly target: HTMLElement | null; public readonly targetPath: Uint8Array; - constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: Element | null) { + constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: HTMLElement | null) { super(ctx, editorPos, pos, relativePos); this._ctx = ctx; @@ -445,13 +445,13 @@ class HitTestRequest extends BareHitTestRequest { return MouseTarget.createOverlayWidget(this.target, this._getMouseColumn(), detail); } - public withTarget(target: Element | null): HitTestRequest { + public withTarget(target: HTMLElement | null): HitTestRequest { return new HitTestRequest(this._ctx, this.editorPos, this.pos, this.relativePos, target); } } interface ResolvedHitTestRequest extends HitTestRequest { - readonly target: Element; + readonly target: HTMLElement; } const EMPTY_CONTENT_AFTER_LINES: IMouseTargetContentEmptyData = { isAfterLines: true }; diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index bc95af4fe9e..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; @@ -553,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); } diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 2cd8aabfa0d..ae6cfd542d3 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -250,6 +250,10 @@ export interface IOverlayWidget { * If null is returned, the overlay widget is responsible to place itself. */ getPosition(): IOverlayWidgetPosition | null; + /** + * The editor will ensure that the scroll width is >= than this value. + */ + getMinContentWidthInPx?(): number; } /** @@ -354,7 +358,7 @@ export interface IBaseMouseTarget { /** * The target element */ - readonly element: Element | null; + readonly element: HTMLElement | null; /** * The 'approximate' editor position */ @@ -1086,6 +1090,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 +1267,9 @@ export interface IDiffEditor extends editorCommon.IEditor { */ revealFirstDiff(): unknown; - diffReviewNext(): void; + accessibleDiffViewerNext(): void; - diffReviewPrev(): void; + accessibleDiffViewerPrev(): void; } /** diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 779468acc0a..75562c53821 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -19,7 +19,7 @@ import { ServicesAccessor as InstantiationServicesAccessor, BrandedService, IIns import { IKeybindings, KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { withNullAsUndefined, assertType } from 'vs/base/common/types'; +import { assertType } from 'vs/base/common/types'; import { ThemeIcon } from 'vs/base/common/themables'; import { IDisposable } from 'vs/base/common/lifecycle'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; @@ -307,7 +307,7 @@ export abstract class EditorCommand extends Command { return editor.invokeWithinContext((editorAccessor) => { const kbService = editorAccessor.get(IContextKeyService); - if (!kbService.contextMatchesRules(withNullAsUndefined(precondition))) { + if (!kbService.contextMatchesRules(precondition ?? undefined)) { // precondition does not hold return; } @@ -460,7 +460,7 @@ export abstract class EditorAction2 extends Action2 { return editor.invokeWithinContext((editorAccessor) => { const kbService = editorAccessor.get(IContextKeyService); const logService = editorAccessor.get(ILogService); - const enabled = kbService.contextMatchesRules(withNullAsUndefined(this.desc.precondition)); + const enabled = kbService.contextMatchesRules(this.desc.precondition ?? undefined); if (!enabled) { logService.debug(`[EditorAction2] NOT running command because its precondition is FALSE`, this.desc.id, this.desc.precondition?.serialize()); return; 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/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 6df2f278f54..e4174a2f286 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -332,11 +332,11 @@ export class ViewLine implements IVisibleLine { return null; } - public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { + public getColumnOfNodeOffset(spanNode: HTMLElement, offset: number): number { if (!this._renderedViewLine) { return 1; } - return this._renderedViewLine.getColumnOfNodeOffset(lineNumber, spanNode, offset); + return this._renderedViewLine.getColumnOfNodeOffset(spanNode, offset); } } @@ -346,7 +346,7 @@ interface IRenderedViewLine { getWidth(context: DomReadingContext | null): number; getWidthIsFast(): boolean; getVisibleRangesForRange(lineNumber: number, startColumn: number, endColumn: number, context: DomReadingContext): FloatHorizontalRange[] | null; - getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number; + getColumnOfNodeOffset(spanNode: HTMLElement, offset: number): number; } const enum Constants { @@ -476,16 +476,8 @@ class FastRenderedViewLine implements IRenderedViewLine { return r[0].left; } - public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { - const spanNodeTextContentLength = spanNode.textContent!.length; - - let spanIndex = -1; - while (spanNode) { - spanNode = spanNode.previousSibling; - spanIndex++; - } - - return this._characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength); + public getColumnOfNodeOffset(spanNode: HTMLElement, offset: number): number { + return getColumnOfNodeOffset(this._characterMapping, spanNode, offset); } } @@ -679,16 +671,8 @@ class RenderedViewLine implements IRenderedViewLine { /** * Returns the column for the text found at a specific offset inside a rendered dom node */ - public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { - const spanNodeTextContentLength = spanNode.textContent!.length; - - let spanIndex = -1; - while (spanNode) { - spanNode = spanNode.previousSibling; - spanIndex++; - } - - return this._characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength); + public getColumnOfNodeOffset(spanNode: HTMLElement, offset: number): number { + return getColumnOfNodeOffset(this._characterMapping, spanNode, offset); } } @@ -733,3 +717,15 @@ function createWebKitRenderedLine(domNode: FastDomNode | null, rend function createNormalRenderedLine(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); } + +export function getColumnOfNodeOffset(characterMapping: CharacterMapping, spanNode: HTMLElement, offset: number): number { + const spanNodeTextContentLength = spanNode.textContent!.length; + + let spanIndex = -1; + while (spanNode) { + spanNode = spanNode.previousSibling; + spanIndex++; + } + + return characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength); +} diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 42f0eb34169..220ef987255 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -364,7 +364,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return null; } - let column = this._visibleLines.getVisibleLine(lineNumber).getColumnOfNodeOffset(lineNumber, spanNode, offset); + let column = this._visibleLines.getVisibleLine(lineNumber).getColumnOfNodeOffset(spanNode, offset); const minColumn = this._context.viewModel.getLineMinColumn(lineNumber); if (column < minColumn) { column = minColumn; 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/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts index 332bdb83728..0165d4eb63f 100644 --- a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts +++ b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts @@ -92,16 +92,19 @@ export class ViewOverlayWidgets extends ViewPart { this._domNode.appendChild(domNode); this.setShouldRender(); + this._updateMaxMinWidth(); } public setWidgetPosition(widget: IOverlayWidget, preference: OverlayWidgetPositionPreference | null): boolean { const widgetData = this._widgets[widget.getId()]; if (widgetData.preference === preference) { + this._updateMaxMinWidth(); return false; } widgetData.preference = preference; this.setShouldRender(); + this._updateMaxMinWidth(); return true; } @@ -115,9 +118,24 @@ export class ViewOverlayWidgets extends ViewPart { domNode.parentNode!.removeChild(domNode); this.setShouldRender(); + this._updateMaxMinWidth(); } } + private _updateMaxMinWidth(): void { + let maxMinWidth = 0; + const keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + const widgetId = keys[i]; + const widget = this._widgets[widgetId]; + const widgetMinWidthInPx = widget.widget.getMinContentWidthInPx?.(); + if (typeof widgetMinWidthInPx !== 'undefined') { + maxMinWidth = Math.max(maxMinWidth, widgetMinWidthInPx); + } + } + this._context.viewLayout.setOverlayWidgetsMinWidth(maxMinWidth); + } + private _renderWidget(widgetData: IWidgetData): void { const domNode = widgetData.domNode; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index e90726fe00c..a6f59231e39 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -48,7 +48,6 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer'; import { WordOperations } from 'vs/editor/common/cursor/cursorWordOperations'; @@ -73,6 +72,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[]; @@ -343,7 +344,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE action.id, action.label, action.alias, - withNullAsUndefined(action.precondition), + action.precondition ?? undefined, (): Promise => { return this._instantiationService.invokeFunction((accessor) => { return Promise.resolve(action.runEditorCommand(accessor, this, null)); @@ -1021,6 +1022,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } } + public handleInitialized(): void { + this._getViewModel()?.visibleLinesStabilized(); + } + public onVisible(): void { this._modelData?.view.refreshFocusState(); } @@ -1847,7 +1852,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._themeService.getColorTheme(), viewModel, viewUserInputEvents, - this._overflowWidgetsDomNode + this._overflowWidgetsDomNode, + this._instantiationService ); return [view, true]; 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..d66333e1aec --- /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, { source: 'accessibleDiffViewer.currentElementChanged' }); + } else if (currentViewItem?.type === LineType.Added) { + this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { source: 'accessibleDiffViewer.currentElementChanged' }); + } + })); + + 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/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 8a821c0b135..5d9af4098f9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -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,13 +43,13 @@ 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 }); @@ -64,8 +65,8 @@ export class DiffEditorDecorations extends Disposable { } else { 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)))) { + && (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; } @@ -104,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], @@ -112,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 b65da185662..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'; @@ -14,8 +14,8 @@ import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOpt import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DiffEditorOptions } from './diffEditorOptions'; -import { IObservable, IReader } from 'vs/base/common/observable'; export class DiffEditorEditors extends Disposable { public readonly modified: CodeEditorWidget; @@ -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, - private readonly _modifiedReadOnlyOverride: IObservable, - @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,6 +47,7 @@ export class DiffEditorEditors extends Disposable { return true; } }, (reader, changeSummary) => { + /** @description update editor options */ _options.editorOptions.read(reader); this.modified.updateOptions(this._adjustOptionsForRightHandSide(reader, changeSummary)); @@ -115,7 +116,6 @@ export class DiffEditorEditors extends Disposable { result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; result.extraEditorClassName = 'modified-in-monaco-diff-editor'; - result.readOnly = this._modifiedReadOnlyOverride.read(reader) || this._options.editorOptions.get().readOnly; return result; } @@ -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 f9d77296cea..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 { @@ -117,7 +117,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo 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.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); } @@ -137,7 +137,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo 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.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); } @@ -146,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); @@ -183,7 +184,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo 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.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); })); } @@ -339,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); @@ -443,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 91f99d55c6b..21b5f67aaae 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,36 +15,43 @@ 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'; import { DiffEditorViewModel, DiffMapping, DiffState } from './diffEditorViewModel'; +import { toDisposable } from 'vs/base/common/lifecycle'; export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly elements = h('div.monaco-diff-editor.side-by-side', { style: { position: 'relative', height: '100%' } }, [ 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 +69,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 +86,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,28 +94,34 @@ 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._register(toDisposable(() => this._domElement.removeChild(this.elements.root))); this._rootSizeObserver = this._register(new ObservableElementSizeObserver(this.elements.root, options.dimension)); this._rootSizeObserver.setAutomaticLayout(options.automaticLayout ?? false); - const reviewPaneObservable = observableValue('reviewPane', undefined); + 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), - reviewPaneObservable.map((r, reader) => r?.isVisible.read(reader) ?? false), + (i, c, o, o2) => this._createInnerEditor(i, c, o, o2) )); this._sash = derivedWithStore('sash', (reader, store) => { @@ -119,7 +136,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); @@ -129,24 +147,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, @@ -157,10 +180,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.actionBarContainer.domNode); - reviewPaneObservable.set(this._reviewPane, undefined); + 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(); @@ -178,22 +214,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); })); @@ -217,6 +252,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, { source: 'diffEditor.cursorPositionChanged' }); + } else if (diff?.lineRangeMapping.originalRange.isEmpty) { + this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { source: 'diffEditor.cursorPositionChanged' }); + } else if (diff) { + this._audioCueService.playAudioCue(AudioCue.diffLineModified, { source: 'diffEditor.cursorPositionChanged' }); + } + } + })); + } + + public getContentHeight() { + return this._editors.modified.getContentHeight(); } protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { @@ -224,26 +276,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(), @@ -316,7 +365,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { override setModel(model: IDiffEditorModel | null | IDiffEditorViewModel): void { if (!model && this._diffModel.get()) { // Transitioning from a model to no-model - this._reviewPane.hide(); + this._accessibleDiffViewer.close(); } const vm = model ? ('model' in model) ? model : this.createViewModel(model) : undefined; @@ -411,6 +460,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, { source: 'diffEditor.goToDiff' }); + } else if (diff.lineRangeMapping.originalRange.isEmpty) { + this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { source: 'diffEditor.goToDiff' }); + } else if (diff) { + this._audioCueService.playAudioCue(AudioCue.diffLineModified, { source: 'diffEditor.goToDiff' }); + } } revealFirstDiff(): void { @@ -428,15 +485,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 82359ffaef0..00000000000 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts +++ /dev/null @@ -1,821 +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 { IObservable, observableValue } from 'vs/base/common/observable'; -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 get _isVisible() { return this._isVisibleObs.get(); } - 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; - - private readonly _isVisibleObs = observableValue('isVisible', false); - - public readonly isVisible: IObservable = this._isVisibleObs; - - constructor( - diffEditor: DiffEditorWidget2, - @ILanguageService private readonly _languageService: ILanguageService, - @IAudioCueService private readonly _audioCueService: IAudioCueService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - super(); - this._diffEditor = diffEditor; - - 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._isVisibleObs.set(true, undefined); - 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._isVisibleObs.set(true, undefined); - 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); - } - } - - public hide(): void { - this._isVisibleObs.set(false, undefined); - 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(); - } - - 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.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; - } - - 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/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts index 12f0c9bafed..92fb780d62c 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'; @@ -70,8 +69,12 @@ export class ViewZoneManager extends Disposable { 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(); } })); + this._register(this._editors.original.onDidChangeConfiguration((args) => { + if (args.hasChanged(EditorOption.wrappingInfo) || args.hasChanged(EditorOption.lineHeight)) { updateImmediately.schedule(); } + })); + this._register(this._editors.modified.onDidChangeConfiguration((args) => { + if (args.hasChanged(EditorOption.wrappingInfo) || args.hasChanged(EditorOption.lineHeight)) { updateImmediately.schedule(); } + })); const originalModelTokenizationCompleted = this._diffModel.map(m => m ? observableFromEvent(m.model.original.onDidChangeTokens, () => m.model.original.tokenization.backgroundTokenizationState === BackgroundTokenizationState.Completed) : undefined @@ -80,21 +83,39 @@ 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 +125,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 +265,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 +276,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 +303,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 +330,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 +363,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 +401,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 +411,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 +422,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 +481,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 +542,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 cc6b9481405..c051508fd84 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts @@ -9,8 +9,7 @@ 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'; @@ -41,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); @@ -51,7 +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()); - 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); 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/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts index 0b221d7c7a4..0f34a1e5960 100644 --- a/src/vs/editor/browser/widget/diffNavigator.ts +++ b/src/vs/editor/browser/widget/diffNavigator.ts @@ -224,11 +224,11 @@ export class DiffNavigator extends Disposable implements IDiffNavigator { } const insertedOrModified = modifiedEditor.getLineDecorations(lineNumber).find(l => l.options.className === 'line-insert'); if (insertedOrModified) { - this._audioCueService.playAudioCue(AudioCue.diffLineModified, true); + this._audioCueService.playAudioCue(AudioCue.diffLineModified, { allowManyInParallel: true }); } else if (jumpToChange) { // The modified editor does not include deleted lines, but when // we are moved to the area where lines were deleted, play this cue - this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, true); + this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, { allowManyInParallel: true }); } else { return; } diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 66ec0c678e1..ea813d85aa5 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -325,9 +325,9 @@ export class DiffReview extends Disposable { } const element = !type ? current : type === 'next' ? current?.nextElementSibling : current?.previousElementSibling; if (element?.classList.contains(DiffEditorLineClasses.Insert)) { - this._audioCueService.playAudioCue(AudioCue.diffLineInserted, true); + this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { allowManyInParallel: true }); } else if (element?.classList.contains(DiffEditorLineClasses.Delete)) { - this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, true); + this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, { allowManyInParallel: true }); } this.scrollbar.scanDomNode(); } 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 96dbb05c275..6895706d518 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -755,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. @@ -827,6 +837,11 @@ export interface IDiffEditorBaseOptions { * Defaults to false */ isInEmbeddedEditor?: boolean; + + /** + * If the diff editor should only show the difference review mode. + */ + onlyShowAccessibleDiffViewer?: boolean; } /** @@ -2737,6 +2752,10 @@ export interface IEditorStickyScrollOptions { * Model to choose for sticky scroll by default */ defaultModel?: 'outlineModel' | 'foldingProviderModel' | 'indentationModel'; + /** + * Define whether to scroll sticky scroll with editor horizontal scrollbae + */ + scrollWithEditor?: boolean; } /** @@ -2747,7 +2766,7 @@ export type EditorStickyScrollOptions = Readonly { constructor() { - const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5, defaultModel: 'outlineModel' }; + const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5, defaultModel: 'outlineModel', scrollWithEditor: true }; super( EditorOption.stickyScroll, 'stickyScroll', defaults, { @@ -2769,6 +2788,11 @@ class EditorStickyScroll extends BaseEditorOption(input.defaultModel, this.defaultValue.defaultModel, ['outlineModel', 'foldingProviderModel', 'indentationModel']), + scrollWithEditor: boolean(input.scrollWithEditor, this.defaultValue.scrollWithEditor) }; } } 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/diff/algorithms/diffAlgorithm.ts b/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts index 8628534a514..ce23a58ceb3 100644 --- a/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts +++ b/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts @@ -68,6 +68,13 @@ export interface ISequence { * Must not be negative. */ getBoundaryScore?(length: number): number; + + /** + * For line sequences, getElement returns a number representing trimmed lines. + * This however checks equality for the original lines. + * It prevents shifting to less matching lines. + */ + isStronglyEqual(offset1: number, offset2: number): boolean; } export interface ITimeout { diff --git a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts index 78fc69c2044..b76c82d371b 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 { LineSequence, LinesSliceCharSequence } from 'vs/editor/common/diff/standardLinesDiffComputer'; export function optimizeSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { let result = sequenceDiffs; @@ -32,6 +33,125 @@ export function smoothenSequenceDiffs(sequence1: ISequence, sequence2: ISequence return result; } +export function removeRandomLineMatches(sequence1: LineSequence, _sequence2: LineSequence, 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 unchangedText = sequence1.getText(unchangedRange); + const unchangedTextWithoutWs = unchangedText.replace(/\s/g, ''); + if (unchangedTextWithoutWs.length <= 4 + && (before.seq1Range.length + before.seq2Range.length > 5 || after.seq1Range.length + after.seq2Range.length > 5)) { + 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; +} + + +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 +165,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) { @@ -171,8 +293,7 @@ function shiftDiffToBetterPosition(diff: SequenceDiff, sequence1: ISequence, seq while ( diff.seq1Range.start - deltaBefore >= seq1ValidRange.start && diff.seq2Range.start - deltaBefore >= seq2ValidRange.start && - sequence2.getElement(diff.seq2Range.start - deltaBefore) === - sequence2.getElement(diff.seq2Range.endExclusive - deltaBefore) && deltaBefore < maxShiftLimit + sequence2.isStronglyEqual(diff.seq2Range.start - deltaBefore, diff.seq2Range.endExclusive - deltaBefore) && deltaBefore < maxShiftLimit ) { deltaBefore++; } @@ -182,8 +303,7 @@ function shiftDiffToBetterPosition(diff: SequenceDiff, sequence1: ISequence, seq while ( diff.seq1Range.start + deltaAfter < seq1ValidRange.endExclusive && diff.seq2Range.endExclusive + deltaAfter < seq2ValidRange.endExclusive && - sequence2.getElement(diff.seq2Range.start + deltaAfter) === - sequence2.getElement(diff.seq2Range.endExclusive + deltaAfter) && deltaAfter < maxShiftLimit + sequence2.isStronglyEqual(diff.seq2Range.start + deltaAfter, diff.seq2Range.endExclusive + deltaAfter) && deltaAfter < maxShiftLimit ) { deltaAfter++; } 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 51f62509b02..f9a4b4a4540 100644 --- a/src/vs/editor/common/diff/standardLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/standardLinesDiffComputer.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { compareBy, findLastIndex, numberComparator, reverseOrder } from 'vs/base/common/arrays'; import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; import { CharCode } from 'vs/base/common/charCode'; +import { SetMap } from 'vs/base/common/collections'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; 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, removeRandomLineMatches, 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'; @@ -59,7 +61,7 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { const sequence2 = new LineSequence(tgtDocLines, modifiedLines); const lineAlignmentResult = (() => { - if (sequence1.length + sequence2.length < 1500) { + if (sequence1.length + sequence2.length < 1700) { // Use the improved algorithm for small files return this.dynamicProgrammingDiffing.compute( sequence1, @@ -83,6 +85,7 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { let lineAlignments = lineAlignmentResult.diffs; let hitTimeout = lineAlignmentResult.hitTimeout; lineAlignments = optimizeSequenceDiffs(sequence1, sequence2, lineAlignments); + lineAlignments = removeRandomLineMatches(sequence1, sequence2, lineAlignments); const alignments: RangeMapping[] = []; @@ -136,45 +139,176 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { const changes = lineRangeMappingFromRangeMappings(alignments, originalLines, modifiedLines); - const moves: MovedText[] = []; + let moves: MovedText[] = []; if (options.computeMoves) { - const deletions = changes - .filter(c => c.modifiedRange.isEmpty && c.originalRange.length >= 3) - .map(d => new LineRangeFragment(d.originalRange, originalLines)); - const insertions = new Set(changes - .filter(c => c.originalRange.isEmpty && c.modifiedRange.length >= 3) - .map(d => new LineRangeFragment(d.modifiedRange, modifiedLines))); + moves = this.computeMoves(changes, originalLines, modifiedLines, srcDocLines, tgtDocLines, timeout, considerWhitespaceChanges); + } - for (const deletion of deletions) { - let highestSimilarity = -1; - let best: LineRangeFragment | undefined; - for (const insertion of insertions) { - const similarity = deletion.computeSimilarity(insertion); - if (similarity > highestSimilarity) { - highestSimilarity = similarity; - best = insertion; - } + // 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 (highestSimilarity > 0.90 && best) { - const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( - new OffsetRange(deletion.range.startLineNumber - 1, deletion.range.endLineNumberExclusive - 1), - new OffsetRange(best.range.startLineNumber - 1, best.range.endLineNumberExclusive - 1), - ), timeout, considerWhitespaceChanges); - const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); - - insertions.delete(best); - moves.push(new MovedText(new SimpleLineRangeMapping(deletion.range, best.range), mappings)); + if (!validateRange(c.modifiedRange, modifiedLines) || !validateRange(c.originalRange, originalLines)) { + return false; } } - } + return true; + }); return new LinesDiff(changes, moves, hitTimeout); } + private computeMoves(changes: LineRangeMapping[], originalLines: string[], modifiedLines: string[], hashedOriginalLines: number[], hashedModifiedLines: number[], timeout: ITimeout, considerWhitespaceChanges: boolean): MovedText[] { + const moves: MovedText[] = []; + const deletions = changes + .filter(c => c.modifiedRange.isEmpty && c.originalRange.length >= 3) + .map(d => new LineRangeFragment(d.originalRange, originalLines, d)); + const insertions = new Set(changes + .filter(c => c.originalRange.isEmpty && c.modifiedRange.length >= 3) + .map(d => new LineRangeFragment(d.modifiedRange, modifiedLines, d))); + + const excludedChanges = new Set(); + + for (const deletion of deletions) { + let highestSimilarity = -1; + let best: LineRangeFragment | undefined; + for (const insertion of insertions) { + const similarity = deletion.computeSimilarity(insertion); + if (similarity > highestSimilarity) { + highestSimilarity = similarity; + best = insertion; + } + } + + if (highestSimilarity > 0.90 && best) { + const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + new OffsetRange(deletion.range.startLineNumber - 1, deletion.range.endLineNumberExclusive - 1), + new OffsetRange(best.range.startLineNumber - 1, best.range.endLineNumberExclusive - 1) + ), timeout, considerWhitespaceChanges); + const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); + + insertions.delete(best); + moves.push(new MovedText(new SimpleLineRangeMapping(deletion.range, best.range), mappings)); + excludedChanges.add(deletion.source); + excludedChanges.add(best.source); + } + } + + const original3LineHashes = new SetMap(); + + for (const change of changes) { + if (excludedChanges.has(change)) { + continue; + } + + for (let i = change.originalRange.startLineNumber; i < change.originalRange.endLineNumberExclusive - 2; i++) { + const key = `${hashedOriginalLines[i - 1]}:${hashedOriginalLines[i + 1 - 1]}:${hashedOriginalLines[i + 2 - 1]}`; + original3LineHashes.add(key, { range: new LineRange(i, i + 3) }); + } + } + + interface PossibleMapping { + modifiedLineRange: LineRange; + originalLineRange: LineRange; + } + + const possibleMappings: PossibleMapping[] = []; + + changes.sort(compareBy(c => c.modifiedRange.startLineNumber, numberComparator)); + + for (const change of changes) { + if (excludedChanges.has(change)) { + continue; + } + + let lastMappings: PossibleMapping[] = []; + for (let i = change.modifiedRange.startLineNumber; i < change.modifiedRange.endLineNumberExclusive - 2; i++) { + const key = `${hashedModifiedLines[i - 1]}:${hashedModifiedLines[i + 1 - 1]}:${hashedModifiedLines[i + 2 - 1]}`; + + //const isWeakKey = (originalLines[i].trim().length + originalLines[i + 1].trim().length + originalLines[i + 2].trim().length < 20); + + const currentModifiedRange = new LineRange(i, i + 3); + + const nextMappings: PossibleMapping[] = []; + original3LineHashes.forEach(key, ({ range }) => { + for (const lastMapping of lastMappings) { + // does this match extend some last match? + if (lastMapping.originalLineRange.endLineNumberExclusive + 1 === range.endLineNumberExclusive && + lastMapping.modifiedLineRange.endLineNumberExclusive + 1 === currentModifiedRange.endLineNumberExclusive) { + lastMapping.originalLineRange = new LineRange(lastMapping.originalLineRange.startLineNumber, range.endLineNumberExclusive); + lastMapping.modifiedLineRange = new LineRange(lastMapping.modifiedLineRange.startLineNumber, currentModifiedRange.endLineNumberExclusive); + nextMappings.push(lastMapping); + return; + } + } + + const mapping: PossibleMapping = { + modifiedLineRange: currentModifiedRange, + originalLineRange: range, + }; + possibleMappings.push(mapping); + nextMappings.push(mapping); + }); + lastMappings = nextMappings; + } + } + + possibleMappings.sort(reverseOrder(compareBy(m => m.modifiedLineRange.length, numberComparator))); + + const modifiedSet = new LineRangeSet(); + const originalSet = new LineRangeSet(); + + for (const mapping of possibleMappings) { + + const diffOrigToMod = mapping.modifiedLineRange.startLineNumber - mapping.originalLineRange.startLineNumber; + const modifiedSections = modifiedSet.subtractFrom(mapping.modifiedLineRange); + const originalTranslatedSections = originalSet.subtractFrom(mapping.originalLineRange).map(r => r.delta(diffOrigToMod)); + + const modifiedIntersectedSections = intersectRanges(modifiedSections, originalTranslatedSections); + + for (const s of modifiedIntersectedSections) { + if (s.length < 3) { + continue; + } + const modifiedLineRange = s; + const originalLineRange = s.delta(-diffOrigToMod); + + const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + new OffsetRange(originalLineRange.startLineNumber - 1, originalLineRange.endLineNumberExclusive - 1), + new OffsetRange(modifiedLineRange.startLineNumber - 1, modifiedLineRange.endLineNumberExclusive - 1) + ), timeout, considerWhitespaceChanges); + const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); + moves.push(new MovedText(new SimpleLineRangeMapping(originalLineRange, modifiedLineRange), mappings)); + + modifiedSet.addRange(modifiedLineRange); + originalSet.addRange(originalLineRange); + } + } + + return moves; + } + 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) @@ -184,6 +318,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) => @@ -202,7 +337,101 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { } } -function coverFullWords(sequence1: Slice, sequence2: Slice, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +function intersectRanges(ranges1: LineRange[], ranges2: LineRange[]): LineRange[] { + const result: LineRange[] = []; + + let i1 = 0; + let i2 = 0; + while (i1 < ranges1.length && i2 < ranges2.length) { + const r1 = ranges1[i1]; + const r2 = ranges2[i2]; + + const i = r1.intersect(r2); + if (i && !i.isEmpty) { + result.push(i); + } + + if (r1.endLineNumberExclusive < r2.endLineNumberExclusive) { + i1++; + } else { + i2++; + } + } + + return result; +} + +// TODO make this fast +class LineRangeSet { + private readonly _normalizedRanges: LineRange[] = []; + + addRange(range: LineRange): void { + // Idea: Find joinRange such that: + // replaceRange = _normalizedRanges.replaceRange(joinRange, range.joinAll(joinRange.map(idx => this._normalizedRanges[idx]))) + + // idx of first element that touches range or that is after range + const joinRangeStartIdx = mapMinusOne(this._normalizedRanges.findIndex(r => r.endLineNumberExclusive >= range.startLineNumber), this._normalizedRanges.length); + // idx of element after { last element that touches range or that is before range } + const joinRangeEndIdxExclusive = findLastIndex(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; + + if (joinRangeStartIdx === joinRangeEndIdxExclusive) { + // If there is no element that touches range, then joinRangeStartIdx === joinRangeEndIdxExclusive and that value is the index of the element after range + this._normalizedRanges.splice(joinRangeStartIdx, 0, range); + } else if (joinRangeStartIdx === joinRangeEndIdxExclusive - 1) { + // Else, there is an element that touches range and in this case it is both the first and last element. Thus we can replace it + const joinRange = this._normalizedRanges[joinRangeStartIdx]; + this._normalizedRanges[joinRangeStartIdx] = joinRange.join(range); + } else { + // First and last element are different - we need to replace the entire range + const joinRange = this._normalizedRanges[joinRangeStartIdx].join(this._normalizedRanges[joinRangeEndIdxExclusive - 1]).join(range); + this._normalizedRanges.splice(joinRangeStartIdx, joinRangeEndIdxExclusive - joinRangeStartIdx, joinRange); + } + } + + intersects(range: LineRange): boolean { + for (const r of this._normalizedRanges) { + if (r.intersectsStrict(range)) { + return true; + } + } + return false; + } + + /** + * Subtracts all ranges in this set from `range` and returns the result. + */ + subtractFrom(range: LineRange): LineRange[] { + // idx of first element that touches range or that is after range + const joinRangeStartIdx = mapMinusOne(this._normalizedRanges.findIndex(r => r.endLineNumberExclusive >= range.startLineNumber), this._normalizedRanges.length); + // idx of element after { last element that touches range or that is before range } + const joinRangeEndIdxExclusive = findLastIndex(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; + + if (joinRangeStartIdx === joinRangeEndIdxExclusive) { + return [range]; + } + + const result: LineRange[] = []; + let startLineNumber = range.startLineNumber; + for (let i = joinRangeStartIdx; i < joinRangeEndIdxExclusive; i++) { + const r = this._normalizedRanges[i]; + if (r.startLineNumber > startLineNumber) { + result.push(new LineRange(startLineNumber, r.startLineNumber)); + } + startLineNumber = r.endLineNumberExclusive; + } + if (startLineNumber < range.endLineNumberExclusive) { + result.push(new LineRange(startLineNumber, range.endLineNumberExclusive)); + } + + return result; + } +} + +function mapMinusOne(idx: number, mapTo: number): number { + return idx === -1 ? mapTo : idx; +} + +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; @@ -407,6 +636,14 @@ export class LineSequence implements ISequence { const indentationAfter = length === this.lines.length ? 0 : getIndentation(this.lines[length]); return 1000 - (indentationBefore + indentationAfter); } + + getText(range: OffsetRange): string { + return this.lines.slice(range.start, range.endExclusive).join('\n'); + } + + isStronglyEqual(offset1: number, offset2: number): boolean { + return this.lines[offset1] === this.lines[offset2]; + } } function getIndentation(str: string): number { @@ -417,7 +654,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; @@ -471,7 +708,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 { @@ -559,6 +800,14 @@ 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; + } + + isStronglyEqual(offset1: number, offset2: number): boolean { + return this.elements[offset1] === this.elements[offset2]; + } } function isWordChar(charCode: number): boolean { @@ -633,6 +882,7 @@ class LineRangeFragment { constructor( public readonly range: LineRange, public readonly lines: string[], + public readonly source: LineRangeMapping, ) { let counter = 0; for (let i = range.startLineNumber - 1; i < range.endLineNumberExclusive - 1; i++) { 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 364998995e3..409b993d05b 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 */ @@ -802,10 +802,10 @@ export interface CodeActionProvider { * @internal */ export interface DocumentPasteEdit { - readonly id: string; readonly label: string; readonly detail: string; - readonly priority: number; + readonly handledMimeType?: string; + readonly yieldTo?: readonly DropYieldTo[]; insertText: string | { readonly snippet: string }; additionalEdit?: WorkspaceEdit; } @@ -815,7 +815,7 @@ export interface DocumentPasteEdit { */ export interface DocumentPasteEditProvider { - readonly id?: string; + readonly id: string; readonly copyMimeTypes?: readonly string[]; readonly pasteMimeTypes?: readonly string[]; @@ -1142,6 +1142,45 @@ export const enum SymbolKind { TypeParameter = 25 } +/** + * @internal + */ +export const symbolKindNames: { [symbol: number]: string } = { + [SymbolKind.Array]: localize('Array', "array"), + [SymbolKind.Boolean]: localize('Boolean', "boolean"), + [SymbolKind.Class]: localize('Class', "class"), + [SymbolKind.Constant]: localize('Constant', "constant"), + [SymbolKind.Constructor]: localize('Constructor', "constructor"), + [SymbolKind.Enum]: localize('Enum', "enumeration"), + [SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"), + [SymbolKind.Event]: localize('Event', "event"), + [SymbolKind.Field]: localize('Field', "field"), + [SymbolKind.File]: localize('File', "file"), + [SymbolKind.Function]: localize('Function', "function"), + [SymbolKind.Interface]: localize('Interface', "interface"), + [SymbolKind.Key]: localize('Key', "key"), + [SymbolKind.Method]: localize('Method', "method"), + [SymbolKind.Module]: localize('Module', "module"), + [SymbolKind.Namespace]: localize('Namespace', "namespace"), + [SymbolKind.Null]: localize('Null', "null"), + [SymbolKind.Number]: localize('Number', "number"), + [SymbolKind.Object]: localize('Object', "object"), + [SymbolKind.Operator]: localize('Operator', "operator"), + [SymbolKind.Package]: localize('Package', "package"), + [SymbolKind.Property]: localize('Property', "property"), + [SymbolKind.String]: localize('String', "string"), + [SymbolKind.Struct]: localize('Struct', "struct"), + [SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"), + [SymbolKind.Variable]: localize('Variable', "variable"), +}; + +/** + * @internal + */ +export function getAriaLabelForSymbol(symbolName: string, kind: SymbolKind): string { + return localize('symbolAriaLabel', '{0} ({1})', symbolName, symbolKindNames[kind]); +} + export const enum SymbolTag { Deprecated = 1, } @@ -1222,6 +1261,13 @@ export interface TextEdit { eol?: model.EndOfLineSequence; } +/** @internal */ +export abstract class TextEdit { + static asEditOperation(edit: TextEdit): ISingleEditOperation { + return EditOperation.replace(Range.lift(edit.range), edit.text); + } +} + /** * Interface used to format a model */ @@ -1962,13 +2008,18 @@ export enum ExternalUriOpenerPriority { Preferred = 3, } +/** + * @internal + */ +export type DropYieldTo = { readonly providerId: string } | { readonly mimeType: string }; + /** * @internal */ export interface DocumentOnDropEdit { - readonly id: string; readonly label: string; - readonly priority: number; + readonly handledMimeType?: string; + readonly yieldTo?: readonly DropYieldTo[]; insertText: string | { readonly snippet: string }; additionalEdit?: WorkspaceEdit; } @@ -1977,6 +2028,7 @@ export interface DocumentOnDropEdit { * @internal */ export interface DocumentOnDropEditProvider { + readonly id?: string; readonly dropMimeTypes?: readonly string[]; provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index deb5fbf628f..0139d8b2055 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IdleDeadline, runWhenIdle } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { BugIndicatingError, onUnexpectedError } from 'vs/base/common/errors'; import { setTimeout0 } from 'vs/base/common/platform'; import { StopWatch } from 'vs/base/common/stopwatch'; import { countEOL } from 'vs/editor/common/core/eolCounter'; @@ -25,7 +25,7 @@ const enum Constants { } export class TokenizerWithStateStore { - 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/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/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 3514fd4cb82..96fa2e77c8c 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -156,6 +156,8 @@ export class ViewLayout extends Disposable implements IViewLayout { private readonly _configuration: IEditorConfiguration; private readonly _linesLayout: LinesLayout; + private _maxLineWidth: number; + private _overlayWidgetsMinWidth: number; private readonly _scrollable: EditorScrollable; public readonly onDidScroll: Event; @@ -170,6 +172,8 @@ export class ViewLayout extends Disposable implements IViewLayout { const padding = options.get(EditorOption.padding); this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight), padding.top, padding.bottom); + this._maxLineWidth = 0; + this._overlayWidgetsMinWidth = 0; this._scrollable = this._register(new EditorScrollable(0, scheduleAtNextAnimationFrame)); this._configureSmoothScrollDuration(); @@ -308,8 +312,9 @@ export class ViewLayout extends Disposable implements IViewLayout { ); } - private _computeContentWidth(maxLineWidth: number): number { + private _computeContentWidth(): number { const options = this._configuration.options; + const maxLineWidth = this._maxLineWidth; const wrappingInfo = options.get(EditorOption.wrappingInfo); const fontInfo = options.get(EditorOption.fontInfo); const layoutInfo = options.get(EditorOption.layoutInfo); @@ -326,16 +331,25 @@ export class ViewLayout extends Disposable implements IViewLayout { } else { const extraHorizontalSpace = options.get(EditorOption.scrollBeyondLastColumn) * fontInfo.typicalHalfwidthCharacterWidth; const whitespaceMinWidth = this._linesLayout.getWhitespaceMinWidth(); - return Math.max(maxLineWidth + extraHorizontalSpace + layoutInfo.verticalScrollbarWidth, whitespaceMinWidth); + return Math.max(maxLineWidth + extraHorizontalSpace + layoutInfo.verticalScrollbarWidth, whitespaceMinWidth, this._overlayWidgetsMinWidth); } } public setMaxLineWidth(maxLineWidth: number): void { + this._maxLineWidth = maxLineWidth; + this._updateContentWidth(); + } + + public setOverlayWidgetsMinWidth(maxMinWidth: number): void { + this._overlayWidgetsMinWidth = maxMinWidth; + this._updateContentWidth(); + } + + private _updateContentWidth(): void { const scrollDimensions = this._scrollable.getScrollDimensions(); - // const newScrollWidth = ; this._scrollable.setScrollDimensions(new EditorScrollDimensions( scrollDimensions.width, - this._computeContentWidth(maxLineWidth), + this._computeContentWidth(), scrollDimensions.height, scrollDimensions.contentHeight )); diff --git a/src/vs/editor/common/viewModel.ts b/src/vs/editor/common/viewModel.ts index ff08d5fa24b..e61ba01dc15 100644 --- a/src/vs/editor/common/viewModel.ts +++ b/src/vs/editor/common/viewModel.ts @@ -114,6 +114,7 @@ export interface IViewLayout { validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition; setMaxLineWidth(maxLineWidth: number): void; + setOverlayWidgetsMinWidth(overlayWidgetsMinWidth: number): void; getLinesViewportData(): IPartialViewLinesViewportData; getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData; 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 5b8d6576b14..14307f031e5 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-no-native-private */ - import { getDomNodePagePosition } from 'vs/base/browser/dom'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAction } from 'vs/base/common/actions'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index 19963bfe57f..b21d5f2dc5d 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-no-native-private */ - import { CancelablePromise, createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; import { isCancellationError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; diff --git a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts index 948e4d7d6bb..035c456ac5a 100644 --- a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts @@ -9,7 +9,6 @@ import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/base/common/themables'; -import { withNullAsUndefined } from 'vs/base/common/types'; import 'vs/css!./lightBulbWidget'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -122,8 +121,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { })); this._register(Event.runAndSubscribe(keybindingService.onDidUpdateKeybindings, () => { - this._preferredKbLabel = withNullAsUndefined(keybindingService.lookupKeybinding(autoFixCommandId)?.getLabel()); - this._quickFixKbLabel = withNullAsUndefined(keybindingService.lookupKeybinding(quickFixCommandId)?.getLabel()); + this._preferredKbLabel = keybindingService.lookupKeybinding(autoFixCommandId)?.getLabel() ?? undefined; + this._quickFixKbLabel = keybindingService.lookupKeybinding(quickFixCommandId)?.getLabel() ?? undefined; this._updateLightBulbTitleAndIcon(); })); 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 3ff93ce28e1..30e79607eb1 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -11,7 +11,6 @@ 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'; @@ -24,7 +23,7 @@ import { Handler, IEditorContribution, PastePayload } from 'vs/editor/common/edi import { DocumentPasteEdit, DocumentPasteEditProvider } from 'vs/editor/common/languages'; import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { createCombinedWorkspaceEdit } from 'vs/editor/contrib/dropOrPasteInto/browser/edit'; +import { createCombinedWorkspaceEdit, sortEditsByYieldTo } from 'vs/editor/contrib/dropOrPasteInto/browser/edit'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState'; import { InlineProgressManager } from 'vs/editor/contrib/inlineProgress/browser/inlineProgress'; import { localize } from 'vs/nls'; @@ -284,12 +283,6 @@ 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); @@ -324,7 +317,11 @@ export class CopyPasteController extends Disposable implements IEditorContributi } // Filter out any providers the don't match the full data transfer we will send them. - const supportedProviders = allProviders.filter(provider => isSupportedPasteProvider(provider, dataTransfer)); + let supportedProviders = allProviders.filter(provider => isSupportedPasteProvider(provider, dataTransfer)); + if (preferredId) { + // We are looking for a specific edit + supportedProviders = supportedProviders.filter(edit => edit.id === preferredId); + } const providerEdits = await this.getPasteEdits(supportedProviders, dataTransfer, model, selections, tokenSource.token); if (tokenSource.token.isCancellationRequested) { @@ -336,14 +333,13 @@ export class CopyPasteController extends Disposable implements IEditorContributi } let pickedEdit: DocumentPasteEdit | undefined; - if (typeof preferredId === 'string') { - // We are looking for a specific edit - pickedEdit = providerEdits.find(edit => edit.id === preferredId); + if (preferredId) { + pickedEdit = providerEdits.at(0); } else { const selected = await this._quickInputService.pick( providerEdits.map((edit): IQuickPickItem & { edit: DocumentPasteEdit } => ({ label: edit.label, - description: edit.id, + description: edit.providerId, detail: edit.detail, edit, })), { @@ -398,7 +394,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi return { defaultPastePayload: { mode: metadata.mode, - multicursorText: withUndefinedAsNull(metadata.multicursorText), + multicursorText: metadata.multicursorText ?? null, pasteOnNewLine: !!metadata.isFromEmptySelection, }, }; @@ -431,19 +427,23 @@ export class CopyPasteController extends Disposable implements IEditorContributi } } - private async getPasteEdits(providers: readonly DocumentPasteEditProvider[], dataTransfer: VSDataTransfer, model: ITextModel, selections: readonly Selection[], token: CancellationToken): Promise { - const result = await raceCancellation( - Promise.all(providers.map(provider => { + private async getPasteEdits(providers: readonly DocumentPasteEditProvider[], dataTransfer: VSDataTransfer, model: ITextModel, selections: readonly Selection[], token: CancellationToken): Promise> { + const results = await raceCancellation( + Promise.all(providers.map(async provider => { try { - return provider.provideDocumentPasteEdits?.(model, selections, dataTransfer, token); + const edit = await provider.provideDocumentPasteEdits?.(model, selections, dataTransfer, token); + if (edit) { + return { ...edit, providerId: provider.id }; + } } catch (err) { console.error(err); - return undefined; } - })).then(coalesce), + return undefined; + })), token); - result?.sort((a, b) => b.priority - a.priority); - return result ?? []; + const edits = coalesce(results ?? []); + sortEditsByYieldTo(edits); + return edits; } private async applyDefaultPasteHandler(dataTransfer: VSDataTransfer, metadata: CopyMetadata | undefined, token: CancellationToken) { diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts index f0f6ac8348d..69ff4dc3181 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts @@ -29,12 +29,12 @@ abstract class SimplePasteAndDropProvider implements DocumentOnDropEditProvider, async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise { const edit = await this.getEdit(dataTransfer, token); - return edit ? { id: this.id, insertText: edit.insertText, label: edit.label, detail: edit.detail, priority: edit.priority } : undefined; + return edit ? { insertText: edit.insertText, label: edit.label, detail: edit.detail, handledMimeType: edit.handledMimeType, yieldTo: edit.yieldTo } : undefined; } async provideDocumentOnDropEdits(_model: ITextModel, _position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise { const edit = await this.getEdit(dataTransfer, token); - return edit ? { id: this.id, insertText: edit.insertText, label: edit.label, priority: edit.priority } : undefined; + return edit ? { insertText: edit.insertText, label: edit.label, handledMimeType: edit.handledMimeType, yieldTo: edit.yieldTo } : undefined; } protected abstract getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise; @@ -46,7 +46,7 @@ class DefaultTextProvider extends SimplePasteAndDropProvider { readonly dropMimeTypes = [Mimes.text]; readonly pasteMimeTypes = [Mimes.text]; - protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, _token: CancellationToken) { + protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, _token: CancellationToken): Promise { const textEntry = dataTransfer.get(Mimes.text); if (!textEntry) { return; @@ -60,8 +60,7 @@ class DefaultTextProvider extends SimplePasteAndDropProvider { const insertText = await textEntry.asString(); return { - id: this.id, - priority: 0, + handledMimeType: Mimes.text, label: localize('text.label', "Insert Plain Text"), detail: builtInLabel, insertText @@ -75,7 +74,7 @@ class PathProvider extends SimplePasteAndDropProvider { readonly dropMimeTypes = [Mimes.uriList]; readonly pasteMimeTypes = [Mimes.uriList]; - protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken) { + protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise { const entries = await extractUriList(dataTransfer); if (!entries.length || token.isCancellationRequested) { return; @@ -107,8 +106,7 @@ class PathProvider extends SimplePasteAndDropProvider { } return { - id: this.id, - priority: 0, + handledMimeType: Mimes.uriList, insertText, label, detail: builtInLabel, @@ -128,7 +126,7 @@ class RelativePathProvider extends SimplePasteAndDropProvider { super(); } - protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken) { + protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise { const entries = await extractUriList(dataTransfer); if (!entries.length || token.isCancellationRequested) { return; @@ -144,8 +142,7 @@ class RelativePathProvider extends SimplePasteAndDropProvider { } return { - id: this.id, - priority: 0, + handledMimeType: Mimes.uriList, insertText: relativeUris.join(' '), label: entries.length > 1 ? localize('defaultDropProvider.uriList.relativePaths', "Insert Relative Paths") diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts index d9ce8328372..ca3ed7dfabb 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts @@ -25,6 +25,7 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { LocalSelectionTransfer } from 'vs/platform/dnd/browser/dnd'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { PostEditWidgetManager } from './postEditWidget'; +import { sortEditsByYieldTo as sortEditsByYieldTo } from './edit'; export const changeDropTypeCommandId = 'editor.changeDropType'; @@ -124,11 +125,19 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } private async getDropEdits(providers: DocumentOnDropEditProvider[], model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, tokenSource: EditorStateCancellationTokenSource) { - const results = await raceCancellation(Promise.all(providers.map(provider => { - return provider.provideDocumentOnDropEdits(model, position, dataTransfer, tokenSource.token); + const results = await raceCancellation(Promise.all(providers.map(async provider => { + try { + const edit = await provider.provideDocumentOnDropEdits(model, position, dataTransfer, tokenSource.token); + if (edit) { + return { ...edit, providerId: provider.id }; + } + } catch (err) { + console.error(err); + } + return undefined; })), tokenSource.token); const edits = coalesce(results ?? []); - edits.sort((a, b) => b.priority - a.priority); + sortEditsByYieldTo(edits); return edits; } diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts index f57ad7c1912..f117d88549a 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; -import { WorkspaceEdit } from 'vs/editor/common/languages'; +import { DropYieldTo, WorkspaceEdit } from 'vs/editor/common/languages'; import { Range } from 'vs/editor/common/core/range'; export interface DropOrPasteEdit { @@ -27,3 +27,26 @@ export function createCombinedWorkspaceEdit(uri: URI, ranges: readonly Range[], ] }; } + +export function sortEditsByYieldTo(edits: T[]): void { + function yieldsTo(yTo: DropYieldTo, other: T): boolean { + return ('providerId' in yTo && yTo.providerId === other.providerId) + || ('mimeType' in yTo && yTo.mimeType === other.handledMimeType); + } + + edits.sort((a, b) => { + if (a.yieldTo?.some(yTo => yieldsTo(yTo, b))) { + return 1; + } + + if (b.yieldTo?.some(yTo => yieldsTo(yTo, a))) { + return -1; + } + + return 0; + }); +} 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/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index 8fe86b49ed8..ee79563f1f5 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -407,7 +407,7 @@ export class FoldingController extends Disposable implements IEditorContribution switch (e.target.type) { case MouseTargetType.GUTTER_LINE_DECORATIONS: { const data = e.target.detail; - const offsetLeftInGutter = (e.target.element as HTMLElement).offsetLeft; + const offsetLeftInGutter = e.target.element!.offsetLeft; const gutterOffsetX = data.offsetX - offsetLeftInGutter; // const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts index a5777a61539..2e74af467a9 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture.ts @@ -100,6 +100,13 @@ function createOptions(multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'): C return new ClickLinkOptions(KeyCode.Alt, 'altKey', KeyCode.Ctrl, 'ctrlKey'); } +export interface IClickLinkGestureOptions { + /** + * Return 0 if the mouse event should not be considered. + */ + extractLineNumberFromMouseEvent?: (e: ClickLinkMouseEvent) => number; +} + export class ClickLinkGesture extends Disposable { private readonly _onMouseMoveOrRelevantKeyDown: Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent | null]> = this._register(new Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent | null]>()); @@ -112,18 +119,18 @@ export class ClickLinkGesture extends Disposable { public readonly onCancel: Event = this._onCancel.event; private readonly _editor: ICodeEditor; - private readonly _alwaysFireExecuteOnMouseUp?: boolean; + private readonly _extractLineNumberFromMouseEvent: (e: ClickLinkMouseEvent) => number; private _opts: ClickLinkOptions; private _lastMouseMoveEvent: ClickLinkMouseEvent | null; private _hasTriggerKeyOnMouseDown: boolean; private _lineNumberOnMouseDown: number; - constructor(editor: ICodeEditor, alwaysFireOnMouseUp?: boolean) { + constructor(editor: ICodeEditor, opts?: IClickLinkGestureOptions) { super(); this._editor = editor; - this._alwaysFireExecuteOnMouseUp = alwaysFireOnMouseUp; + this._extractLineNumberFromMouseEvent = opts?.extractLineNumberFromMouseEvent ?? ((e) => e.target.position ? e.target.position.lineNumber : 0); this._opts = createOptions(this._editor.getOption(EditorOption.multiCursorModifier)); this._lastMouseMoveEvent = null; @@ -178,12 +185,12 @@ export class ClickLinkGesture extends Disposable { // release the mouse button without wanting to do the navigation. // With this flag we prevent goto definition if the mouse was down before the trigger key was pressed. this._hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier; - this._lineNumberOnMouseDown = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0; + this._lineNumberOnMouseDown = this._extractLineNumberFromMouseEvent(mouseEvent); } private _onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void { - const currentLineNumber = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0; - if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber || this._alwaysFireExecuteOnMouseUp) { + const currentLineNumber = this._extractLineNumberFromMouseEvent(mouseEvent); + if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber) { this._onExecute.fire(mouseEvent); } } diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts index a71163b6deb..f5a08f94c1e 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts @@ -9,7 +9,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { withNullAsUndefined } from 'vs/base/common/types'; import 'vs/css!./goToDefinitionAtPosition'; import { CodeEditorStateFlag, EditorState } from 'vs/editor/contrib/editorState/browser/editorState'; import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -58,7 +57,7 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri this.toUnhook.add(linkGesture); this.toUnhook.add(linkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { - this.startFindDefinitionFromMouse(mouseEvent, withNullAsUndefined(keyboardEvent)); + this.startFindDefinitionFromMouse(mouseEvent, keyboardEvent ?? undefined); })); this.toUnhook.add(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => { diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts index ca4597bc57e..d23e38b81ab 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts @@ -305,7 +305,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { useShadows: true, verticalHasArrows: false, horizontalHasArrows: false, - alwaysConsumeMouseWheel: false + alwaysConsumeMouseWheel: true }, overviewRulerLanes: 2, fixedOverflowWidgets: true, diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 12c126e30bd..841c5cd2a34 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { HoverAction, HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget'; +import { HoverAction, HoverWidget, getHoverAriaLabel } from 'vs/base/browser/ui/hover/hoverWidget'; import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -24,13 +24,16 @@ import { AsyncIterableObject } from 'vs/base/common/async'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ResizableContentWidget } from 'vs/editor/contrib/hover/browser/resizableContentWidget'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; + const $ = dom.$; 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 +55,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 +223,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 +292,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() }; @@ -477,11 +494,19 @@ export class ContentHoverWidget extends ResizableContentWidget { return this._hoverVisibleKey.get() ?? false; } + public get isFocused(): boolean { + return this._hoverFocusedKey.get() ?? false; + } + constructor( editor: ICodeEditor, - @IContextKeyService contextKeyService: IContextKeyService + minimumSize: dom.Dimension, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @IKeybindingService private readonly _keybindingService: IKeybindingService ) { - super(editor); + super(editor, minimumSize); this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(contextKeyService); this._hoverFocusedKey = EditorContextKeys.hoverFocused.bindTo(contextKeyService); @@ -504,6 +529,8 @@ export class ContentHoverWidget extends ResizableContentWidget { this._setHoverData(undefined); this._layout(); this._editor.addContentWidget(this); + + this._hover.containerDomNode.ariaLabel = getHoverAriaLabel(this._configurationService.getValue('accessibility.verbosity.hover') === true && this._accessibilityService.isScreenReaderOptimized(), this._keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel()) ?? ''; } public override dispose(): void { @@ -539,12 +566,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 { @@ -562,7 +595,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); @@ -577,6 +610,7 @@ 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 { @@ -669,13 +703,11 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _layout(): void { - 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); 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(width, height); + this._updateMaxDimensions(); } private _updateFont(): void { @@ -695,17 +727,17 @@ export class ContentHoverWidget extends ResizableContentWidget { this._hover.onContentsChanged(); } - private _updateContentsDomNodeMaxDimensions() { + 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._setContentsDomNodeMaxDimensions(width, height); + 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' @@ -778,6 +810,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; @@ -796,6 +832,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(); this._initialWidth = this._hover.containerDomNode.clientWidth; } diff --git a/src/vs/editor/contrib/hover/browser/hover.ts b/src/vs/editor/contrib/hover/browser/hover.ts index 46c94e313df..2c1e2b2611e 100644 --- a/src/vs/editor/contrib/hover/browser/hover.ts +++ b/src/vs/editor/contrib/hover/browser/hover.ts @@ -155,6 +155,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 +175,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 +186,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 +220,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 +233,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 +246,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 +262,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,8 +303,12 @@ export class ModesHoverController implements IEditorContribution { this._contentWidget?.goToBottom(); } - public isHoverVisible(): boolean | undefined { - return this._contentWidget?.isVisible(); + public get isColorPickerVisible(): boolean | undefined { + return this._contentWidget?.isColorPickerVisible; + } + + public get isHoverVisible(): boolean | undefined { + return this._contentWidget?.isVisible; } public dispose(): void { @@ -367,7 +372,7 @@ 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); 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/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 b027df1b39c..1e9a7fbbbd0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts @@ -28,18 +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); - this.inlineCompletionVisible.set(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 01946f09986..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); } } @@ -279,10 +285,10 @@ export class InlineCompletionsModel extends Disposable { } const state = this.state.get(); - if (!state || state.ghostText.isEmpty() || !state.completion) { + if (!state || state.ghostText.isEmpty() || !state.inlineCompletion) { return; } - const completion = state.completion.toInlineCompletion(undefined); + const completion = state.inlineCompletion.toInlineCompletion(undefined); editor.pushUndoStop(); if (completion.snippetInfo) { @@ -371,11 +377,11 @@ export class InlineCompletionsModel extends Disposable { } const state = this.state.get(); - if (!state || state.ghostText.isEmpty() || !state.completion) { + if (!state || state.ghostText.isEmpty() || !state.inlineCompletion) { return; } const ghostText = state.ghostText; - const completion = state.completion.toInlineCompletion(undefined); + 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 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/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/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts index 4336914a3ce..d4082fc5a27 100644 --- a/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts @@ -7,7 +7,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; import { DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { getCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IRange } from 'vs/editor/common/core/range'; import { IDiffEditor, IEditor, ScrollType } from 'vs/editor/common/editorCommon'; @@ -95,9 +94,9 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu // changes even later because it could be that the user has // configured quick access to remain open when focus is lost and // we always want to restore the current location. - let lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState()); + let lastKnownEditorViewState = editor.saveViewState() ?? undefined; disposables.add(codeEditor.onDidChangeCursorPosition(() => { - lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState()); + lastKnownEditorViewState = editor.saveViewState() ?? undefined; })); context.restoreViewState = () => { 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/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts index 78b6a87e8b2..80a9959f475 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -7,7 +7,6 @@ import { groupBy } from 'vs/base/common/arrays'; import { CharCode } from 'vs/base/common/charCode'; import { dispose } from 'vs/base/common/lifecycle'; import { getLeadingWhitespace } from 'vs/base/common/strings'; -import { withNullAsUndefined } from 'vs/base/common/types'; import 'vs/css!./snippetSession'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -346,7 +345,7 @@ export class OneSnippet { let result: Range | undefined; const model = this._editor.getModel(); for (const decorationId of this._placeholderDecorations!.values()) { - const placeholderRange = withNullAsUndefined(model.getDecorationRange(decorationId)); + const placeholderRange = model.getDecorationRange(decorationId) ?? undefined; if (!result) { result = placeholderRange; } else { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 757fc92d81d..8b1c053ad78 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -3,23 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .sticky-line { - color: var(--vscode-editorLineNumber-foreground); +.monaco-editor .sticky-widget { overflow: hidden; +} + +.monaco-editor .sticky-widget-line-numbers { + float: left; + background-color: inherit; +} + +.monaco-editor .sticky-widget-lines-scrollable { + display: inline-block; + position: absolute; + overflow: hidden; + width: var(--vscode-editorStickyScroll-scrollableWidth); + background-color: inherit; +} + +.monaco-editor .sticky-widget-lines { + position: absolute; + background-color: inherit; +} + +.monaco-editor .sticky-line-number, .monaco-editor .sticky-line-content { + color: var(--vscode-editorLineNumber-foreground); white-space: nowrap; display: inline-block; -} - -.monaco-editor .sticky-line-number { - text-align: right; - float: left; -} - -.monaco-editor .sticky-line-root { + position: absolute; + background-color: inherit; +} + +.monaco-editor .sticky-line-content { + width: var(--vscode-editorStickyScroll-scrollableWidth); background-color: inherit; - overflow: hidden; white-space: nowrap; - width: 100%; +} + +.monaco-editor .sticky-line-number-inner { + display: block; + text-align: right; } .monaco-editor.hc-black .sticky-widget, @@ -27,7 +49,7 @@ border-bottom: 1px solid var(--vscode-contrastBorder); } -.monaco-editor .sticky-line-root:hover { +.monaco-editor .sticky-line-content:hover { background-color: var(--vscode-editorStickyScrollHover-background); cursor: pointer; } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index fb7adfb064a..265e0c458ff 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; @@ -16,7 +16,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { MenuId } from 'vs/platform/actions/common/actions'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ClickLinkGesture } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture'; +import { ClickLinkGesture, ClickLinkMouseEvent } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture'; import { IRange, Range } from 'vs/editor/common/core/range'; import { getDefinitionsAtPosition } from 'vs/editor/contrib/gotoSymbol/browser/goToSymbol'; import { goToDefinitionWithLocation } from 'vs/editor/contrib/inlayHints/browser/inlayHintsLocations'; @@ -26,11 +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'; - -interface CustomMouseEvent { - detail: string; - element: HTMLElement; -} +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export interface IStickyScrollController { get stickyScrollCandidateProvider(): IStickyLineCandidateProvider; @@ -61,7 +57,6 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _stickyScrollFocusedContextKey: IContextKey; private _stickyScrollVisibleContextKey: IContextKey; - private _stickyElements: HTMLCollection | undefined; private _focusDisposableStore: DisposableStore | undefined; private _focusedStickyElementIndex: number = -1; private _enabled = false; @@ -86,22 +81,22 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._widgetState = new StickyScrollWidgetState([], 0); this._readConfiguration(); + const stickyScrollDomNode = this._stickyScrollWidget.getDomNode(); this._register(this._editor.onDidChangeConfiguration(e => { - if (e.hasChanged(EditorOption.stickyScroll)) { + if (e.hasChanged(EditorOption.stickyScroll) || e.hasChanged(EditorOption.minimap)) { this._readConfiguration(); } })); - this._register(dom.addDisposableListener(this._stickyScrollWidget.getDomNode(), dom.EventType.CONTEXT_MENU, async (event: MouseEvent) => { + this._register(dom.addDisposableListener(stickyScrollDomNode, dom.EventType.CONTEXT_MENU, async (event: MouseEvent) => { this._onContextMenu(event); })); this._stickyScrollFocusedContextKey = EditorContextKeys.stickyScrollFocused.bindTo(this._contextKeyService); this._stickyScrollVisibleContextKey = EditorContextKeys.stickyScrollVisible.bindTo(this._contextKeyService); - const focusTracker = this._register(dom.trackFocus(this._stickyScrollWidget.getDomNode())); + const focusTracker = this._register(dom.trackFocus(stickyScrollDomNode)); this._register(focusTracker.onDidBlur(_ => { - const height = this._stickyScrollWidget.getDomNode().clientHeight; // Suppose that the blurring is caused by scrolling, then keep the focus on the sticky scroll // This is determined by the fact that the height of the widget has become zero and there has been no position revealing - if (this._positionRevealed === false && height === 0) { + if (this._positionRevealed === false && stickyScrollDomNode.clientHeight === 0) { this._focusedStickyElementIndex = -1; this.focus(); @@ -114,9 +109,9 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._register(focusTracker.onDidFocus(_ => { this.focus(); })); - this._register(this._createClickLinkGesture()); + this._registerMouseListeners(); // Suppose that mouse down on the sticky scroll, then do not focus on the sticky scroll because this will be followed by the revealing of a position - this._register(dom.addDisposableListener(this._stickyScrollWidget.getDomNode(), dom.EventType.MOUSE_DOWN, (e) => { + this._register(dom.addDisposableListener(stickyScrollDomNode, dom.EventType.MOUSE_DOWN, (e) => { this._onMouseDown = true; })); } @@ -155,14 +150,12 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._focused = true; this._focusDisposableStore = new DisposableStore(); this._stickyScrollFocusedContextKey.set(true); - const rootNode = this._stickyScrollWidget.getDomNode(); - (rootNode.lastElementChild! as HTMLDivElement).focus(); - this._stickyElements = rootNode.children; this._focusedStickyElementIndex = this._stickyScrollWidget.lineNumbers.length - 1; + this._stickyScrollWidget.focusLineWithIndex(this._focusedStickyElementIndex); } public focusNext(): void { - if (this._focusedStickyElementIndex < this._stickyElements!.length - 1) { + if (this._focusedStickyElementIndex < this._stickyScrollWidget.lineNumberCount - 1) { this._focusNav(true); } } @@ -180,7 +173,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib // True is next, false is previous private _focusNav(direction: boolean): void { this._focusedStickyElementIndex = direction ? this._focusedStickyElementIndex + 1 : this._focusedStickyElementIndex - 1; - (this._stickyElements!.item(this._focusedStickyElementIndex) as HTMLDivElement).focus(); + this._stickyScrollWidget.focusLineWithIndex(this._focusedStickyElementIndex); } public goToFocused(): void { @@ -196,101 +189,142 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._editor.focus(); } - private _createClickLinkGesture(): IDisposable { + private _registerMouseListeners(): void { - const linkGestureStore = new DisposableStore(); - const sessionStore = new DisposableStore(); - linkGestureStore.add(sessionStore); - const gesture = new ClickLinkGesture(this._editor, true); - linkGestureStore.add(gesture); + const sessionStore = this._register(new DisposableStore()); + const gesture = this._register(new ClickLinkGesture(this._editor, { + extractLineNumberFromMouseEvent: (e) => { + const position = this._stickyScrollWidget.getEditorPositionFromNode(e.target.element); + return position ? position.lineNumber : 0; + } + })); - linkGestureStore.add(gesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, _keyboardEvent]) => { - if (!this._editor.hasModel() || !mouseEvent.hasTriggerModifier) { + const getMouseEventTarget = (mouseEvent: ClickLinkMouseEvent): { range: Range; textElement: HTMLElement } | null => { + if (!this._editor.hasModel()) { + return null; + } + if (mouseEvent.target.type !== MouseTargetType.OVERLAY_WIDGET || mouseEvent.target.detail !== this._stickyScrollWidget.getId()) { + // not hovering over our widget + return null; + } + const mouseTargetElement = mouseEvent.target.element; + if (!mouseTargetElement || mouseTargetElement.innerText !== mouseTargetElement.innerHTML) { + // not on a span element rendering text + return null; + } + const position = this._stickyScrollWidget.getEditorPositionFromNode(mouseTargetElement); + if (!position) { + // not hovering a sticky scroll line + return null; + } + return { + range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + mouseTargetElement.innerText.length), + textElement: mouseTargetElement + }; + }; + + this._register(this._editor.onMouseUp((mouseEvent: IEditorMouseEvent) => { + if (mouseEvent.target.type !== MouseTargetType.OVERLAY_WIDGET || mouseEvent.target.detail !== this._stickyScrollWidget.getId()) { + // not hovering over our widget + return; + } + if (mouseEvent.event.ctrlKey || mouseEvent.event.shiftKey || mouseEvent.event.altKey || mouseEvent.event.metaKey) { + // modifier pressed + return; + } + if (!mouseEvent.event.leftButton) { + // not left click + return; + } + + let position = this._stickyScrollWidget.getEditorPositionFromNode(mouseEvent.target.element); + if (!position) { + const lineNumber = this._stickyScrollWidget.getLineNumberFromChildDomNode(mouseEvent.target.element); + if (lineNumber === null) { + // not hovering a sticky scroll line + return; + } + position = new Position(lineNumber, 1); + } + + if (this._focused) { + this._disposeFocusStickyScrollStore(); + } + this._revealPosition(position); + })); + + this._register(gesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, _keyboardEvent]) => { + const mouseTarget = getMouseEventTarget(mouseEvent); + if (!mouseTarget || !mouseEvent.hasTriggerModifier || !this._editor.hasModel()) { sessionStore.clear(); return; } - const targetMouseEvent = mouseEvent.target as unknown as CustomMouseEvent; - if (targetMouseEvent.detail === this._stickyScrollWidget.getId() - && targetMouseEvent.element.innerText === targetMouseEvent.element.innerHTML) { + const { range, textElement } = mouseTarget; - const text = targetMouseEvent.element.innerText; - if (this._stickyScrollWidget.hoverOnColumn === -1) { - return; - } - const lineNumber = this._stickyScrollWidget.hoverOnLine; - const column = this._stickyScrollWidget.hoverOnColumn; - - const stickyPositionProjectedOnEditor = new Range(lineNumber, column, lineNumber, column + text.length); - if (!stickyPositionProjectedOnEditor.equalsRange(this._stickyRangeProjectedOnEditor)) { - this._stickyRangeProjectedOnEditor = stickyPositionProjectedOnEditor; - sessionStore.clear(); - } else if (targetMouseEvent.element.style.textDecoration === 'underline') { - return; - } - - const cancellationToken = new CancellationTokenSource(); - sessionStore.add(toDisposable(() => cancellationToken.dispose(true))); - - let currentHTMLChild: HTMLElement; - - getDefinitionsAtPosition(this._languageFeaturesService.definitionProvider, this._editor.getModel(), new Position(lineNumber, column + 1), cancellationToken.token).then((candidateDefinitions => { - if (cancellationToken.token.isCancellationRequested) { - return; - } - if (candidateDefinitions.length !== 0) { - this._candidateDefinitionsLength = candidateDefinitions.length; - const childHTML: HTMLElement = targetMouseEvent.element; - if (currentHTMLChild !== childHTML) { - sessionStore.clear(); - currentHTMLChild = childHTML; - currentHTMLChild.style.textDecoration = 'underline'; - sessionStore.add(toDisposable(() => { - currentHTMLChild.style.textDecoration = 'none'; - })); - } else if (!currentHTMLChild) { - currentHTMLChild = childHTML; - currentHTMLChild.style.textDecoration = 'underline'; - sessionStore.add(toDisposable(() => { - currentHTMLChild.style.textDecoration = 'none'; - })); - } - } else { - sessionStore.clear(); - } - })); - } else { + if (!range.equalsRange(this._stickyRangeProjectedOnEditor)) { + this._stickyRangeProjectedOnEditor = range; sessionStore.clear(); + } else if (textElement.style.textDecoration === 'underline') { + return; } + + const cancellationToken = new CancellationTokenSource(); + sessionStore.add(toDisposable(() => cancellationToken.dispose(true))); + + let currentHTMLChild: HTMLElement; + + getDefinitionsAtPosition(this._languageFeaturesService.definitionProvider, this._editor.getModel(), new Position(range.startLineNumber, range.startColumn + 1), cancellationToken.token).then((candidateDefinitions => { + if (cancellationToken.token.isCancellationRequested) { + return; + } + if (candidateDefinitions.length !== 0) { + this._candidateDefinitionsLength = candidateDefinitions.length; + const childHTML: HTMLElement = textElement; + if (currentHTMLChild !== childHTML) { + sessionStore.clear(); + currentHTMLChild = childHTML; + currentHTMLChild.style.textDecoration = 'underline'; + sessionStore.add(toDisposable(() => { + currentHTMLChild.style.textDecoration = 'none'; + })); + } else if (!currentHTMLChild) { + currentHTMLChild = childHTML; + currentHTMLChild.style.textDecoration = 'underline'; + sessionStore.add(toDisposable(() => { + currentHTMLChild.style.textDecoration = 'none'; + })); + } + } else { + sessionStore.clear(); + } + })); })); - linkGestureStore.add(gesture.onCancel(() => { + this._register(gesture.onCancel(() => { sessionStore.clear(); })); - linkGestureStore.add(gesture.onExecute(async e => { - if ((e.target as unknown as CustomMouseEvent).detail !== this._stickyScrollWidget.getId()) { + this._register(gesture.onExecute(async e => { + if (e.target.type !== MouseTargetType.OVERLAY_WIDGET || e.target.detail !== this._stickyScrollWidget.getId()) { + // not hovering over our widget return; } - if (e.hasTriggerModifier) { - // Control click - if (this._candidateDefinitionsLength > 1) { - if (this._focused) { - this._disposeFocusStickyScrollStore(); - } - this._revealPosition({ lineNumber: this._stickyScrollWidget.hoverOnLine, column: 1 }); - } - this._instaService.invokeFunction(goToDefinitionWithLocation, e, this._editor as IActiveCodeEditor, { uri: this._editor.getModel()!.uri, range: this._stickyRangeProjectedOnEditor! }); - - } else if (!e.isRightClick) { - // Normal click + const position = this._stickyScrollWidget.getEditorPositionFromNode(e.target.element); + if (!position) { + // not hovering a sticky scroll line + return; + } + if (this._candidateDefinitionsLength > 1) { if (this._focused) { this._disposeFocusStickyScrollStore(); } - this._revealPosition({ lineNumber: this._stickyScrollWidget.hoverOnLine, column: this._stickyScrollWidget.hoverOnColumn }); + this._revealPosition({ lineNumber: position.lineNumber, column: 1 }); } + this._instaService.invokeFunction(goToDefinitionWithLocation, e, this._editor as IActiveCodeEditor, { uri: this._editor.getModel()!.uri, range: this._stickyRangeProjectedOnEditor! }); })); - return linkGestureStore; } - private _onContextMenu(event: MouseEvent) { + private _onContextMenu(e: MouseEvent) { + const event = new StandardMouseEvent(e); + this._contextMenuService.showContextMenu({ menuId: MenuId.StickyScrollContext, getAnchor: () => event, @@ -308,7 +342,11 @@ export class StickyScrollController extends Disposable implements IEditorContrib } else if (options.enabled && !this._enabled) { // When sticky scroll was just enabled, add the listeners on the sticky scroll this._editor.addOverlayWidget(this._stickyScrollWidget); - this._sessionStore.add(this._editor.onDidScrollChange(() => this._renderStickyScroll())); + this._sessionStore.add(this._editor.onDidScrollChange((e) => { + if (e.scrollTopChanged) { + this._renderStickyScroll(); + } + })); this._sessionStore.add(this._editor.onDidLayoutChange(() => this._onDidResize())); this._sessionStore.add(this._editor.onDidChangeModelTokens((e) => this._onTokensChange(e))); this._sessionStore.add(this._stickyLineCandidateProvider.onDidChangeStickyScroll(() => this._renderStickyScroll())); @@ -341,8 +379,6 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _onDidResize() { const layoutInfo = this._editor.getLayoutInfo(); - const width = layoutInfo.width - layoutInfo.minimap.minimapCanvasOuterWidth - layoutInfo.verticalScrollbarWidth; - this._stickyScrollWidget.getDomNode().style.width = `${width}px`; // Make sure sticky scroll doesn't take up more than 25% of the editor const theoreticalLines = layoutInfo.height / this._editor.getOption(EditorOption.lineHeight); this._maxStickyLines = Math.round(theoreticalLines * .25); @@ -361,19 +397,18 @@ export class StickyScrollController extends Disposable implements IEditorContrib if (!this._focused) { this._stickyScrollWidget.setState(this._widgetState); } else { - this._stickyElements = this._stickyScrollWidget.getDomNode().children; // Suppose that previously the sticky scroll widget had height 0, then if there are visible lines, set the last line as focused if (this._focusedStickyElementIndex === -1) { this._stickyScrollWidget.setState(this._widgetState); - this._focusedStickyElementIndex = this._stickyElements.length - 1; + this._focusedStickyElementIndex = this._stickyScrollWidget.lineNumberCount - 1; if (this._focusedStickyElementIndex !== -1) { - (this._stickyElements.item(this._focusedStickyElementIndex) as HTMLDivElement).focus(); + this._stickyScrollWidget.focusLineWithIndex(this._focusedStickyElementIndex); } } else { const focusedStickyElementLineNumber = this._stickyScrollWidget.lineNumbers[this._focusedStickyElementIndex]; this._stickyScrollWidget.setState(this._widgetState); // Suppose that after setting the state, there are no sticky lines, set the focused index to -1 - if (this._stickyElements.length === 0) { + if (this._stickyScrollWidget.lineNumberCount === 0) { this._focusedStickyElementIndex = -1; } else { const previousFocusedLineNumberExists = this._stickyScrollWidget.lineNumbers.includes(focusedStickyElementLineNumber); @@ -381,9 +416,9 @@ export class StickyScrollController extends Disposable implements IEditorContrib // If the line number is still there, do not change anything // If the line number is not there, set the new focused line to be the last line if (!previousFocusedLineNumberExists) { - this._focusedStickyElementIndex = this._stickyElements.length - 1; + this._focusedStickyElementIndex = this._stickyScrollWidget.lineNumberCount - 1; } - (this._stickyElements.item(this._focusedStickyElementIndex) as HTMLDivElement).focus(); + this._stickyScrollWidget.focusLineWithIndex(this._focusedStickyElementIndex); } } } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts index cf549710a75..13051e80058 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts @@ -120,6 +120,9 @@ export class StickyModelProvider implements IStickyModelProvider { } } return null; + }).catch((error) => { + onUnexpectedError(error); + return null; }); } } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 7c7260203bf..7880cb5f103 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -4,68 +4,89 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; -import { equals } from 'vs/base/common/arrays'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import 'vs/css!./stickyScroll'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { getColumnOfNodeOffset } from 'vs/editor/browser/viewParts/lines/viewLine'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { EditorLayoutInfo, EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; -import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { CharacterMapping, RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; export class StickyScrollWidgetState { constructor( readonly lineNumbers: number[], readonly lastLineRelativePosition: number ) { } - - public equals(other: StickyScrollWidgetState | undefined): boolean { - return !!other && this.lastLineRelativePosition === other.lastLineRelativePosition && equals(this.lineNumbers, other.lineNumbers); - } } const _ttPolicy = createTrustedTypesPolicy('stickyScrollViewLayer', { createHTML: value => value }); +const STICKY_LINE_INDEX_ATTR = 'data-sticky-line-index'; export class StickyScrollWidget extends Disposable implements IOverlayWidget { - private readonly _layoutInfo: EditorLayoutInfo; private readonly _rootDomNode: HTMLElement = document.createElement('div'); - private readonly _disposableStore = this._register(new DisposableStore()); + private readonly _lineNumbersDomNode: HTMLElement = document.createElement('div'); + private readonly _linesDomNodeScrollable: HTMLElement = document.createElement('div'); + private readonly _linesDomNode: HTMLElement = document.createElement('div'); + private _stickyLines: RenderedStickyLine[] = []; private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; - private _hoverOnLine: number = -1; - private _hoverOnColumn: number = -1; - private _state: StickyScrollWidgetState | undefined; + private _minContentWidthInPx: number = 0; constructor( private readonly _editor: ICodeEditor ) { super(); - this._layoutInfo = this._editor.getLayoutInfo(); - this._rootDomNode = document.createElement('div'); + + this._lineNumbersDomNode.className = 'sticky-widget-line-numbers'; + this._lineNumbersDomNode.setAttribute('role', 'none'); + + this._linesDomNode.className = 'sticky-widget-lines'; + this._linesDomNode.setAttribute('role', 'list'); + + this._linesDomNodeScrollable.className = 'sticky-widget-lines-scrollable'; + this._linesDomNodeScrollable.appendChild(this._linesDomNode); + this._rootDomNode.className = 'sticky-widget'; this._rootDomNode.classList.toggle('peek', _editor instanceof EmbeddedCodeEditorWidget); - this._rootDomNode.style.width = `${this._layoutInfo.width - this._layoutInfo.minimap.minimapCanvasOuterWidth - this._layoutInfo.verticalScrollbarWidth}px`; - } + this._rootDomNode.appendChild(this._lineNumbersDomNode); + this._rootDomNode.appendChild(this._linesDomNodeScrollable); - get hoverOnLine(): number { - return this._hoverOnLine; - } + const updateScrollLeftPosition = () => { + this._linesDomNode.style.left = this._editor.getOption(EditorOption.stickyScroll).scrollWithEditor ? `-${this._editor.getScrollLeft()}px` : '0px'; + }; + this._register(this._editor.onDidChangeConfiguration((e) => { + if (e.hasChanged(EditorOption.stickyScroll)) { + updateScrollLeftPosition(); + } + })); + this._register(this._editor.onDidScrollChange((e) => { + if (e.scrollLeftChanged) { + updateScrollLeftPosition(); + } + if (e.scrollWidthChanged) { + this._updateWidgetWidth(); + } + })); + this._register(this._editor.onDidChangeModel(() => updateScrollLeftPosition())); + updateScrollLeftPosition(); - get hoverOnColumn(): number { - return this._hoverOnColumn; + this._register(this._editor.onDidLayoutChange((e) => { + this._updateWidgetWidth(); + })); + this._updateWidgetWidth(); } get lineNumbers(): number[] { return this._lineNumbers; } - get codeLineCount(): number { + get lineNumberCount(): number { return this._lineNumbers.length; } @@ -74,13 +95,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } setState(state: StickyScrollWidgetState): void { - if (state.equals(this._state)) { - return; - } - this._state = state; - dom.clearNode(this._rootDomNode); - this._disposableStore.clear(); - this._lineNumbers.length = 0; + dom.clearNode(this._lineNumbersDomNode); + dom.clearNode(this._linesDomNode); + this._stickyLines = []; const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); const futureWidgetHeight = state.lineNumbers.length * editorLineHeight + state.lastLineRelativePosition; @@ -94,36 +111,50 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._renderRootNode(); } + private _updateWidgetWidth(): void { + const layoutInfo = this._editor.getLayoutInfo(); + const minimapSide = this._editor.getOption(EditorOption.minimap).side; + const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; + this._lineNumbersDomNode.style.width = `${lineNumbersWidth}px`; + this._linesDomNodeScrollable.style.setProperty('--vscode-editorStickyScroll-scrollableWidth', `${this._editor.getScrollWidth() - layoutInfo.verticalScrollbarWidth}px`); + this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.minimap.minimapCanvasOuterWidth - layoutInfo.verticalScrollbarWidth}px`; + } + private _renderRootNode(): void { if (!this._editor._getViewModel()) { return; } + + const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { - const childNode = this._renderChildNode(index, line); - this._rootDomNode.appendChild(childNode); + const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo); + this._lineNumbersDomNode.appendChild(lineNumberHTMLNode); + this._linesDomNode.appendChild(renderedStickyLine.domNode); + this._stickyLines.push(renderedStickyLine); } const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); const widgetHeight: number = this._lineNumbers.length * editorLineHeight + this._lastLineRelativePosition; this._rootDomNode.style.display = widgetHeight > 0 ? 'block' : 'none'; - this._rootDomNode.style.height = widgetHeight.toString() + 'px'; - this._rootDomNode.setAttribute('role', 'list'); + this._lineNumbersDomNode.style.height = `${widgetHeight}px`; + this._linesDomNodeScrollable.style.height = `${widgetHeight}px`; + this._rootDomNode.style.height = `${widgetHeight}px`; const minimapSide = this._editor.getOption(EditorOption.minimap).side; if (minimapSide === 'left') { - this._rootDomNode.style.marginLeft = this._editor.getLayoutInfo().minimap.minimapCanvasOuterWidth + 'px'; + this._rootDomNode.style.marginLeft = layoutInfo.minimap.minimapCanvasOuterWidth + 'px'; + } else { + this._rootDomNode.style.marginLeft = '0px'; } + this._updateMinContentWidth(); + this._editor.layoutOverlayWidget(this); } - private _renderChildNode(index: number, line: number): HTMLDivElement { - - const child = document.createElement('div'); + private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); - const layoutInfo = this._editor.getLayoutInfo(); - const width = layoutInfo.width - layoutInfo.minimap.minimapCanvasOuterWidth - layoutInfo.verticalScrollbarWidth; const minimapSide = this._editor.getOption(EditorOption.minimap).side; const lineHeight = this._editor.getOption(EditorOption.lineHeight); const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); @@ -144,7 +175,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { ); const sb = new StringBuilder(2000); - renderViewLine(renderLineInput, sb); + const renderOutput = renderViewLine(renderLineInput, sb); let newLine; if (_ttPolicy) { @@ -154,19 +185,14 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } const lineHTMLNode = document.createElement('span'); - lineHTMLNode.className = 'sticky-line'; + lineHTMLNode.className = 'sticky-line-content'; lineHTMLNode.classList.add(`stickyLine${line}`); lineHTMLNode.style.lineHeight = `${lineHeight}px`; lineHTMLNode.innerHTML = newLine as string; const lineNumberHTMLNode = document.createElement('span'); - lineNumberHTMLNode.className = 'sticky-line'; + lineNumberHTMLNode.className = 'sticky-line-number'; lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; - if (minimapSide === 'left') { - lineNumberHTMLNode.style.width = `${layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth}px`; - } else if (minimapSide === 'right') { - lineNumberHTMLNode.style.width = `${layoutInfo.contentLeft}px`; - } const innerLineNumberHTML = document.createElement('span'); if (lineNumberOption.renderType === RenderLineNumbersType.On || lineNumberOption.renderType === RenderLineNumbersType.Interval && line % 10 === 0) { @@ -174,7 +200,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } else if (lineNumberOption.renderType === RenderLineNumbersType.Relative) { innerLineNumberHTML.innerText = Math.abs(line - this._editor.getPosition()!.lineNumber).toString(); } - innerLineNumberHTML.className = 'sticky-line-number'; + innerLineNumberHTML.className = 'sticky-line-number-inner'; innerLineNumberHTML.style.lineHeight = `${lineHeight}px`; innerLineNumberHTML.style.width = `${layoutInfo.lineNumbersWidth}px`; if (minimapSide === 'left') { @@ -187,38 +213,42 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._editor.applyFontInfo(lineHTMLNode); this._editor.applyFontInfo(innerLineNumberHTML); - child.appendChild(lineNumberHTMLNode); - child.appendChild(lineHTMLNode); + lineHTMLNode.setAttribute('role', 'listitem'); + lineHTMLNode.setAttribute(STICKY_LINE_INDEX_ATTR, String(index)); + lineHTMLNode.tabIndex = 0; - child.className = 'sticky-line-root'; - child.setAttribute('role', 'listitem'); - child.tabIndex = 0; - child.style.lineHeight = `${lineHeight}px`; - child.style.width = `${width}px`; - child.style.height = `${lineHeight}px`; - child.style.zIndex = '0'; + lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; + lineHTMLNode.style.lineHeight = `${lineHeight}px`; + lineNumberHTMLNode.style.height = `${lineHeight}px`; + lineHTMLNode.style.height = `${lineHeight}px`; // Special case for the last line of sticky scroll - if (index === this._lineNumbers.length - 1) { - child.style.position = 'relative'; - child.style.zIndex = '-1'; - child.style.top = this._lastLineRelativePosition + 'px'; - } + const isLastLine = index === this._lineNumbers.length - 1; - // Each child has a listener which fires when the mouse hovers over the child - this._disposableStore.add(dom.addDisposableListener(child, 'mouseover', (e) => { - if (this._editor.hasModel()) { - const mouseOverEvent = new StandardMouseEvent(e); - const text = mouseOverEvent.target.innerText; + const lastLineZIndex = '0'; + const intermediateLineZIndex = '1'; + lineHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; + lineNumberHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; - // Line and column number of the hover needed for the control clicking feature - this._hoverOnLine = line; - // TODO: workaround to find the column index, perhaps need a more solid solution - this._hoverOnColumn = this._editor.getModel().getLineContent(line).indexOf(text) + 1 || -1; + const lastLineTop = `${index * lineHeight + this._lastLineRelativePosition}px`; + const intermediateLineTop = `${index * lineHeight}px`; + lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; + lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; + + return { + lineNumberHTMLNode, + renderedStickyLine: new RenderedStickyLine(line, lineHTMLNode, renderOutput.characterMapping) + }; + } + + private _updateMinContentWidth() { + this._minContentWidthInPx = 0; + for (const stickyLine of this._stickyLines) { + if (stickyLine.domNode.scrollWidth > this._minContentWidthInPx) { + this._minContentWidthInPx = stickyLine.domNode.scrollWidth; } - })); - - return child; + } + this._minContentWidthInPx += this._editor.getLayoutInfo().verticalScrollbarWidth; } getId(): string { @@ -234,4 +264,65 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { preference: null }; } + + getMinContentWidthInPx(): number { + return this._minContentWidthInPx; + } + + focusLineWithIndex(index: number) { + if (0 <= index && index < this._stickyLines.length) { + this._stickyLines[index].domNode.focus(); + } + } + + /** + * Given a leaf dom node, tries to find the editor position. + */ + getEditorPositionFromNode(spanDomNode: HTMLElement | null): Position | null { + if (!spanDomNode || spanDomNode.children.length > 0) { + // This is not a leaf node + return null; + } + const renderedStickyLine = this._getRenderedStickyLineFromChildDomNode(spanDomNode); + if (!renderedStickyLine) { + return null; + } + const column = getColumnOfNodeOffset(renderedStickyLine.characterMapping, spanDomNode, 0); + return new Position(renderedStickyLine.lineNumber, column); + } + + getLineNumberFromChildDomNode(domNode: HTMLElement | null): number | null { + return this._getRenderedStickyLineFromChildDomNode(domNode)?.lineNumber ?? null; + } + + private _getRenderedStickyLineFromChildDomNode(domNode: HTMLElement | null): RenderedStickyLine | null { + const index = this._getStickyLineIndexFromChildDomNode(domNode); + if (index === null || index < 0 || index >= this._stickyLines.length) { + return null; + } + return this._stickyLines[index]; + } + + /** + * Given a child dom node, tries to find the line number attribute + * that was stored in the node. Returns null if none is found. + */ + private _getStickyLineIndexFromChildDomNode(domNode: HTMLElement | null): number | null { + while (domNode && domNode !== this._rootDomNode) { + const line = domNode.getAttribute(STICKY_LINE_INDEX_ATTR); + if (line) { + return parseInt(line, 10); + } + domNode = domNode.parentElement; + } + return null; + } +} + +class RenderedStickyLine { + constructor( + public readonly lineNumber: number, + public readonly domNode: HTMLElement, + public readonly characterMapping: CharacterMapping + ) { } } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index d2e86783c8a..09ebd2bdda0 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -34,6 +34,7 @@ import { CompletionItem, Context as SuggestContext, suggestWidgetStatusbarMenu } import { canExpandCompletionItem, SuggestDetailsOverlay, SuggestDetailsWidget } from './suggestWidgetDetails'; import { getAriaId, ItemRenderer } from './suggestWidgetRenderer'; import { getListStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { status } from 'vs/base/browser/ui/aria/aria'; /** * Suggest widget colors @@ -237,9 +238,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); } @@ -463,6 +464,7 @@ export class SuggestWidget implements IDisposable { this._details.hide(); this._show(); this._focusedItem = undefined; + status(SuggestWidget.LOADING_MESSAGE); break; case State.Empty: this.element.domNode.classList.add('message'); @@ -472,6 +474,7 @@ export class SuggestWidget implements IDisposable { this._details.hide(); this._show(); this._focusedItem = undefined; + status(SuggestWidget.NO_SUGGESTIONS_MESSAGE); break; case State.Open: dom.hide(this._messageElement); 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/quickAccess/standaloneCommandsQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts index 5e87aa8f710..171a395e6f3 100644 --- a/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts +++ b/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts @@ -10,7 +10,6 @@ import { ICommandQuickPick } from 'vs/platform/quickinput/browser/commandsQuickA import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { AbstractEditorCommandsQuickAccessProvider } from 'vs/editor/contrib/quickAccess/browser/commandsQuickAccess'; import { IEditor } from 'vs/editor/common/editorCommon'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -24,7 +23,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; export class StandaloneCommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { - protected get activeTextEditorControl(): IEditor | undefined { return withNullAsUndefined(this.codeEditorService.getFocusedCodeEditor()); } + protected get activeTextEditorControl(): IEditor | undefined { return this.codeEditorService.getFocusedCodeEditor() ?? undefined; } constructor( @IInstantiationService instantiationService: IInstantiationService, diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts index 71cfc2ec4ba..c6ccecf960c 100644 --- a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts +++ b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts @@ -7,7 +7,6 @@ import { AbstractGotoLineQuickAccessProvider } from 'vs/editor/contrib/quickAcce import { Registry } from 'vs/platform/registry/common/platform'; import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { GoToLineNLS } from 'vs/editor/common/standaloneStrings'; import { Event } from 'vs/base/common/event'; import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; @@ -25,7 +24,7 @@ export class StandaloneGotoLineQuickAccessProvider extends AbstractGotoLineQuick } protected get activeTextEditorControl() { - return withNullAsUndefined(this.editorService.getFocusedCodeEditor()); + return this.editorService.getFocusedCodeEditor() ?? undefined; } } diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts index d7bf0d94002..0c2cfb8617b 100644 --- a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts +++ b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts @@ -9,7 +9,6 @@ import { AbstractGotoSymbolQuickAccessProvider } from 'vs/editor/contrib/quickAc import { Registry } from 'vs/platform/registry/common/platform'; import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { QuickOutlineNLS } from 'vs/editor/common/standaloneStrings'; import { Event } from 'vs/base/common/event'; import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; @@ -34,7 +33,7 @@ export class StandaloneGotoSymbolQuickAccessProvider extends AbstractGotoSymbolQ } protected get activeTextEditorControl() { - return withNullAsUndefined(this.editorService.getFocusedCodeEditor()); + return this.editorService.getFocusedCodeEditor() ?? undefined; } } 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..e7297361013 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'; @@ -1039,7 +1039,7 @@ class StandaloneContextMenuService extends ContextMenuService { class StandaloneAudioService implements IAudioCueService { _serviceBrand: undefined; - async playAudioCue(cue: AudioCue, allowManyInParallel?: boolean | undefined): Promise { + async playAudioCue(cue: AudioCue, options: {}): Promise { } async playAudioCues(cues: AudioCue[]): Promise { @@ -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/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/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/bracket-aligning/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/bracket-aligning/advanced.expected.diff.json index 18845aa6ccd..85083dedd28 100644 --- a/src/vs/editor/test/node/diffing/fixtures/bracket-aligning/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/bracket-aligning/advanced.expected.diff.json @@ -67,8 +67,8 @@ ] }, { - "originalRange": "[86,99)", - "modifiedRange": "[88,102)", + "originalRange": "[86,95)", + "modifiedRange": "[88,98)", "innerChanges": [ { "originalRange": "[86,1 -> 86,1]", @@ -108,23 +108,7 @@ }, { "originalRange": "[95,1 -> 95,1]", - "modifiedRange": "[97,1 -> 97,2]" - }, - { - "originalRange": "[96,1 -> 96,1]", - "modifiedRange": "[98,1 -> 98,2]" - }, - { - "originalRange": "[97,1 -> 97,1]", - "modifiedRange": "[99,1 -> 99,2]" - }, - { - "originalRange": "[98,1 -> 98,1]", - "modifiedRange": "[100,1 -> 100,2]" - }, - { - "originalRange": "[99,1 -> 99,1]", - "modifiedRange": "[101,1 -> 102,1]" + "modifiedRange": "[97,1 -> 98,1]" } ] } 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..2b8f1b4ab1a 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 @@ -9,64 +9,22 @@ }, "diffs": [ { - "originalRange": "[29,34)", - "modifiedRange": "[29,31)", + "originalRange": "[29,61)", + "modifiedRange": "[29,37)", "innerChanges": [ { - "originalRange": "[29,1 -> 33,1]", - "modifiedRange": "[29,1 -> 29,1]" - }, - { - "originalRange": "[33,14 -> 33,41]", - "modifiedRange": "[29,14 -> 30,54]" + "originalRange": "[29,1 -> 60,48]", + "modifiedRange": "[29,1 -> 36,66]" } ] }, { - "originalRange": "[35,36)", - "modifiedRange": "[32,33)", - "innerChanges": [ - { - "originalRange": "[35,2 -> 35,26]", - "modifiedRange": "[32,2 -> 32,48]" - } - ] - }, - { - "originalRange": "[37,40)", - "modifiedRange": "[34,35)", - "innerChanges": [ - { - "originalRange": "[37,2 -> 39,3]", - "modifiedRange": "[34,2 -> 34,64]" - } - ] - }, - { - "originalRange": "[41,46)", - "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": "[48,72)", + "originalRange": "[63,72)", "modifiedRange": "[39,40)", "innerChanges": [ { - "originalRange": "[48,9 -> 63,48]", - "modifiedRange": "[39,9 -> 39,43]" + "originalRange": "[63,9 -> 63,43]", + "modifiedRange": "[39,9 -> 39,38]" }, { "originalRange": "[64,1 -> 72,1]", 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/fuzzy-matching/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/fuzzy-matching/advanced.expected.diff.json index ed6c0fe7ff1..4059da96da8 100644 --- a/src/vs/editor/test/node/diffing/fixtures/fuzzy-matching/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/fuzzy-matching/advanced.expected.diff.json @@ -9,22 +9,40 @@ }, "diffs": [ { - "originalRange": "[1,1)", - "modifiedRange": "[1,9)", + "originalRange": "[1,9)", + "modifiedRange": "[1,10)", "innerChanges": [ { - "originalRange": "[1,1 -> 1,1]", - "modifiedRange": "[1,1 -> 9,1]" - } - ] - }, - { - "originalRange": "[2,9)", - "modifiedRange": "[10,10)", - "innerChanges": [ + "originalRange": "[1,1 -> 2,1]", + "modifiedRange": "[1,1 -> 1,1]" + }, { - "originalRange": "[2,1 -> 9,1]", - "modifiedRange": "[10,1 -> 10,1]" + "originalRange": "[2,15 -> 2,15]", + "modifiedRange": "[1,15 -> 1,16]" + }, + { + "originalRange": "[3,15 -> 3,15]", + "modifiedRange": "[2,15 -> 2,16]" + }, + { + "originalRange": "[4,15 -> 4,15]", + "modifiedRange": "[3,15 -> 3,16]" + }, + { + "originalRange": "[5,15 -> 5,15]", + "modifiedRange": "[4,15 -> 5,1]" + }, + { + "originalRange": "[6,15 -> 6,15]", + "modifiedRange": "[6,15 -> 6,16]" + }, + { + "originalRange": "[7,15 -> 7,15]", + "modifiedRange": "[7,15 -> 7,16]" + }, + { + "originalRange": "[8,15 -> 8,15]", + "modifiedRange": "[8,15 -> 9,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/indentation/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/indentation/advanced.expected.diff.json index 2e7031437fa..43176254538 100644 --- a/src/vs/editor/test/node/diffing/fixtures/indentation/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/indentation/advanced.expected.diff.json @@ -27,8 +27,8 @@ ] }, { - "originalRange": "[12,24)", - "modifiedRange": "[13,26)", + "originalRange": "[12,23)", + "modifiedRange": "[13,25)", "innerChanges": [ { "originalRange": "[12,1 -> 12,1]", @@ -76,11 +76,7 @@ }, { "originalRange": "[23,1 -> 23,1]", - "modifiedRange": "[24,1 -> 24,2]" - }, - { - "originalRange": "[24,1 -> 24,1]", - "modifiedRange": "[25,1 -> 26,1]" + "modifiedRange": "[24,1 -> 25,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/issue-185779/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/issue-185779/advanced.expected.diff.json index d3a24ad44f2..a7ea515c057 100644 --- a/src/vs/editor/test/node/diffing/fixtures/issue-185779/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/issue-185779/advanced.expected.diff.json @@ -9,8 +9,8 @@ }, "diffs": [ { - "originalRange": "[26,33)", - "modifiedRange": "[26,43)", + "originalRange": "[26,32)", + "modifiedRange": "[26,42)", "innerChanges": [ { "originalRange": "[26,10 -> 26,10]", @@ -38,11 +38,7 @@ }, { "originalRange": "[32,1 -> 32,1]", - "modifiedRange": "[41,1 -> 41,2]" - }, - { - "originalRange": "[33,1 -> 33,1]", - "modifiedRange": "[42,1 -> 43,1]" + "modifiedRange": "[41,1 -> 42,1]" } ] } 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..8ac0682b508 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 @@ -9,50 +9,24 @@ }, "diffs": [ { - "originalRange": "[6,10)", - "modifiedRange": "[6,8)", + "originalRange": "[6,15)", + "modifiedRange": "[6,11)", "innerChanges": [ { - "originalRange": "[6,2 -> 6,9]", - "modifiedRange": "[6,2 -> 6,11]" + "originalRange": "[6,2 -> 11,49]", + "modifiedRange": "[6,2 -> 6,91]" }, { - "originalRange": "[6,12 -> 6,18]", - "modifiedRange": "[6,14 -> 6,14]" + "originalRange": "[12,76 -> 12,76]", + "modifiedRange": "[7,76 -> 7,80]" }, { - "originalRange": "[6,29 -> 6,54]", - "modifiedRange": "[6,25 -> 6,91]" + "originalRange": "[12,153 -> 12,153]", + "modifiedRange": "[7,157 -> 7,181]" }, { - "originalRange": "[6,57 -> 6,75]", - "modifiedRange": "[6,94 -> 6,118]" - }, - { - "originalRange": "[7,9 -> 7,38]", - "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": "[11,15)", - "modifiedRange": "[9,11)", - "innerChanges": [ - { - "originalRange": "[11,1 -> 13,1]", - "modifiedRange": "[9,1 -> 9,1]" + "originalRange": "[13,1 -> 13,1]", + "modifiedRange": "[8,1 -> 9,1]" }, { "originalRange": "[13,31 -> 13,31]", 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..f6817defb49 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,44 +43,48 @@ "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]" } ] }, { "originalRange": "[17,24)", - "modifiedRange": "[20,24)", + "modifiedRange": "[20,22)", "innerChanges": [ { "originalRange": "[17,1 -> 17,1]", "modifiedRange": "[20,1 -> 21,1]" }, { - "originalRange": "[17,8 -> 17,40]", - "modifiedRange": "[21,8 -> 21,43]" + "originalRange": "[17,9 -> 17,21]", + "modifiedRange": "[21,9 -> 21,15]" }, { - "originalRange": "[17,71 -> 17,72]", - "modifiedRange": "[21,74 -> 22,1]" + "originalRange": "[17,24 -> 17,40]", + "modifiedRange": "[21,18 -> 21,43]" }, { - "originalRange": "[18,3 -> 23,4]", - "modifiedRange": "[23,3 -> 23,120]" + "originalRange": "[17,71 -> 23,4]", + "modifiedRange": "[21,74 -> 21,81]" } ] }, { "originalRange": "[25,27)", - "modifiedRange": "[25,25)", + "modifiedRange": "[23,25)", "innerChanges": [ { - "originalRange": "[25,1 -> 27,1]", - "modifiedRange": "[25,1 -> 25,1]" + "originalRange": "[25,9 -> 26,18]", + "modifiedRange": "[23,9 -> 23,18]" + }, + { + "originalRange": "[26,22 -> 26,27]", + "modifiedRange": "[23,22 -> 23,23]" + }, + { + "originalRange": "[27,1 -> 27,1]", + "modifiedRange": "[24,1 -> 25,1]" } ] }, @@ -89,20 +93,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..d8eac8523e6 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 @@ -13,12 +13,8 @@ "modifiedRange": "[9,14)", "innerChanges": [ { - "originalRange": "[9,10 -> 9,10]", - "modifiedRange": "[9,10 -> 9,68]" - }, - { - "originalRange": "[9,15 -> 9,15]", - "modifiedRange": "[9,73 -> 13,16]" + "originalRange": "[9,10 -> 9,15]", + "modifiedRange": "[9,10 -> 13,16]" }, { "originalRange": "[9,35 -> 9,44]", @@ -31,16 +27,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/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json index 92c6e475761..85a9a2fe928 100644 --- 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 @@ -23,28 +23,8 @@ "modifiedRange": "[7,18)", "innerChanges": [ { - "originalRange": "[7,5 -> 7,43]", - "modifiedRange": "[7,5 -> 11,140]" - }, - { - "originalRange": "[8,5 -> 9,12]", - "modifiedRange": "[12,5 -> 12,131]" - }, - { - "originalRange": "[10,7 -> 10,22]", - "modifiedRange": "[13,7 -> 13,17]" - }, - { - "originalRange": "[10,30 -> 10,48]", - "modifiedRange": "[13,25 -> 13,118]" - }, - { - "originalRange": "[11,6 -> 11,13]", - "modifiedRange": "[14,6 -> 14,8]" - }, - { - "originalRange": "[12,5 -> 12,17]", - "modifiedRange": "[15,5 -> 16,7]" + "originalRange": "[7,5 -> 12,17]", + "modifiedRange": "[7,5 -> 16,7]" }, { "originalRange": "[13,6 -> 13,11]", @@ -53,4 +33,4 @@ ] } ] -} +} \ 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